From 8286e587877f1e62932734e9ca8f75763f12135a Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Fri, 27 Jul 2012 12:00:06 -0700 Subject: [PATCH 01/72] New framework for tile caching section of the docs --- doc/en/user/source/webadmin/server/index.rst | 1 - .../geowebcache.rst => tilecache/defaults.rst} | 0 .../user/source/webadmin/tilecache/gridsets.rst | 0 .../{server => tilecache}/img/gwcsettings.png | Bin doc/en/user/source/webadmin/tilecache/index.rst | 16 ++++++++++++++++ .../user/source/webadmin/tilecache/layers.rst | 0 .../user/source/webadmin/tilecache/quotas.rst | 0 7 files changed, 16 insertions(+), 1 deletion(-) rename doc/en/user/source/webadmin/{server/geowebcache.rst => tilecache/defaults.rst} (100%) create mode 100644 doc/en/user/source/webadmin/tilecache/gridsets.rst rename doc/en/user/source/webadmin/{server => tilecache}/img/gwcsettings.png (100%) create mode 100644 doc/en/user/source/webadmin/tilecache/index.rst create mode 100644 doc/en/user/source/webadmin/tilecache/layers.rst create mode 100644 doc/en/user/source/webadmin/tilecache/quotas.rst diff --git a/doc/en/user/source/webadmin/server/index.rst b/doc/en/user/source/webadmin/server/index.rst index 58753d9558f..88f4206b2b7 100644 --- a/doc/en/user/source/webadmin/server/index.rst +++ b/doc/en/user/source/webadmin/server/index.rst @@ -11,6 +11,5 @@ The Server section of the :ref:`web_admin` provides access to GeoServer environm status contact globalsettings - geowebcache coverageaccess JAI diff --git a/doc/en/user/source/webadmin/server/geowebcache.rst b/doc/en/user/source/webadmin/tilecache/defaults.rst similarity index 100% rename from doc/en/user/source/webadmin/server/geowebcache.rst rename to doc/en/user/source/webadmin/tilecache/defaults.rst diff --git a/doc/en/user/source/webadmin/tilecache/gridsets.rst b/doc/en/user/source/webadmin/tilecache/gridsets.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/doc/en/user/source/webadmin/server/img/gwcsettings.png b/doc/en/user/source/webadmin/tilecache/img/gwcsettings.png similarity index 100% rename from doc/en/user/source/webadmin/server/img/gwcsettings.png rename to doc/en/user/source/webadmin/tilecache/img/gwcsettings.png diff --git a/doc/en/user/source/webadmin/tilecache/index.rst b/doc/en/user/source/webadmin/tilecache/index.rst new file mode 100644 index 00000000000..ae37f1b5643 --- /dev/null +++ b/doc/en/user/source/webadmin/tilecache/index.rst @@ -0,0 +1,16 @@ +.. _webadmin_tilecache: + +Tile Cache +========== + +This section of the :ref:`web_admin` allows for setting of the tile caching options for GeoServer. GeoServer uses GeoWebCache to provide direct and integrated tile caching. + +For more information on GeoServer's integrated tile cache, please see the section on :ref:`geowebcache`. + +.. toctree:: + :maxdepth: 1 + + layers + defaults + gridsets + diskquotas \ No newline at end of file diff --git a/doc/en/user/source/webadmin/tilecache/layers.rst b/doc/en/user/source/webadmin/tilecache/layers.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/doc/en/user/source/webadmin/tilecache/quotas.rst b/doc/en/user/source/webadmin/tilecache/quotas.rst new file mode 100644 index 00000000000..e69de29bb2d From 559febf52ae078096d6d1fc923109f6f72888ae4 Mon Sep 17 00:00:00 2001 From: aaime Date: Wed, 1 Aug 2012 08:52:11 +0200 Subject: [PATCH 02/72] Move the controlflow documentation in the extensions section --- doc/en/user/source/community/index.rst | 1 - .../user/source/{community => extensions}/controlflow/index.rst | 0 doc/en/user/source/extensions/index.rst | 1 + 3 files changed, 1 insertion(+), 1 deletion(-) rename doc/en/user/source/{community => extensions}/controlflow/index.rst (100%) diff --git a/doc/en/user/source/community/index.rst b/doc/en/user/source/community/index.rst index 700015abcb4..157e413531e 100644 --- a/doc/en/user/source/community/index.rst +++ b/doc/en/user/source/community/index.rst @@ -16,7 +16,6 @@ officially part of the GeoServer releases. They are however built along with the :maxdepth: 1 authkey/index - controlflow/index css/index dxf/index dds/index diff --git a/doc/en/user/source/community/controlflow/index.rst b/doc/en/user/source/extensions/controlflow/index.rst similarity index 100% rename from doc/en/user/source/community/controlflow/index.rst rename to doc/en/user/source/extensions/controlflow/index.rst diff --git a/doc/en/user/source/extensions/index.rst b/doc/en/user/source/extensions/index.rst index 40720ab56d7..79b32b062ba 100644 --- a/doc/en/user/source/extensions/index.rst +++ b/doc/en/user/source/extensions/index.rst @@ -13,6 +13,7 @@ see the :ref:`data_vector`, :ref:`data_raster`, and :ref:`data_database` section .. toctree:: :maxdepth: 2 + controlflow/index excel geosearch imagemap From d1920138a9f9c145576f5c7dd4d0190fda102036 Mon Sep 17 00:00:00 2001 From: aaime Date: Wed, 1 Aug 2012 08:52:55 +0200 Subject: [PATCH 03/72] [GEOS-5242] Improve logging for control-flow --- .../geoserver/flow/ControlFlowCallback.java | 89 ++++++++++++++----- .../DefaultControlFlowConfigurator.java | 2 + .../flow/controller/QueueController.java | 8 +- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java index c9f461ab639..d4081333a6d 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -40,14 +41,23 @@ public class ControlFlowCallback extends AbstractDispatcherCallback implements long timeout = -1; ControlFlowConfigurator configurator; + + AtomicLong blockedRequests = new AtomicLong(); + + AtomicLong runningRequests = new AtomicLong(); public void finished(Request request) { if(SENTINEL.isOutermostRequest()) { + runningRequests.decrementAndGet(); // call back the same controllers we used when the operation started if (REQUEST_CONTROLLERS.get() != null) { List fcl = REQUEST_CONTROLLERS.get(); for (FlowController flowController : fcl) { - flowController.requestComplete(request); + try { + flowController.requestComplete(request); + } catch(Exception e) { + LOGGER.log(Level.SEVERE, "Flow controller " + fcl + " failed to mark the request as complete", e); + } } } // clean up the thread local @@ -57,6 +67,48 @@ public void finished(Request request) { } public Operation operationDispatched(Request request, Operation operation) { + checkConfiguration(); + + // tell the recursion sentinel we're starting a request + SENTINEL.start(); + if(SENTINEL.isOutermostRequest()) { + blockedRequests.incrementAndGet(); + try { + // scan through the existing controllers and set the list in a thread local + // so that this request will get exactly the same list when the operation finishes + List controllers = this.controllers; + if (controllers.size() > 0) { + REQUEST_CONTROLLERS.set(controllers); + long maxTime = timeout > 0 ? System.currentTimeMillis() + timeout : -1; + for (FlowController flowController : controllers) { + if(timeout > 0) { + long maxWait = maxTime - System.currentTimeMillis(); + if(!flowController.requestIncoming(request, maxWait)) + throw new HttpErrorCodeException(503, "Requested timeout out while waiting to be executed"); + } else { + flowController.requestIncoming(request, -1); + } + } + } + } finally { + blockedRequests.decrementAndGet(); + runningRequests.incrementAndGet(); + + // provide some info + if(LOGGER.isLoggable(Level.INFO)) { + if(controllers.size() > 0) { + LOGGER.info("Running requests: " + runningRequests.get() + + ", processing through flow controllers: " + blockedRequests.get()); + } else { + LOGGER.info("Control flow installed, but no rules configured in controlflow.properties"); + } + } + } + } + return operation; + } + + private void checkConfiguration() { // check if we need to rebuild the flow controller list if (configurator.isStale()){ // be careful, as the configuration can be read on demand, it'd not be uncommon that @@ -67,28 +119,6 @@ public Operation operationDispatched(Request request, Operation operation) { } } } - - // tell the recursion sentinel we're starting a request - SENTINEL.start(); - if(SENTINEL.isOutermostRequest()) { - // scan through the existing controllers and set the list in a thread local - // so that this request will get exactly the same list when the operation finishes - List controllers = this.controllers; - if (controllers.size() > 0) { - REQUEST_CONTROLLERS.set(controllers); - long maxTime = timeout > 0 ? System.currentTimeMillis() + timeout : -1; - for (FlowController flowController : controllers) { - if(timeout > 0) { - long maxWait = maxTime - System.currentTimeMillis(); - if(!flowController.requestIncoming(request, maxWait)) - throw new HttpErrorCodeException(503, "Requested timeout out while waiting to be executed"); - } else { - flowController.requestIncoming(request, -1); - } - } - } - } - return operation; } /** @@ -100,6 +130,12 @@ void reloadConfiguration() { .buildFlowControllers()); Collections.sort(newControllers, new ControllerPriorityComparator()); controllers = newControllers; + int controllersCount = controllers.size(); + if(controllersCount > 0) { + LOGGER.info("Control-flow active with " + controllersCount + " flow controllers"); + } else { + LOGGER.info("Control-flow inactive, there are no configured rules"); + } timeout = configurator.getTimeout(); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error occurerd during flow controllers reconfiguration"); @@ -110,8 +146,13 @@ public void setApplicationContext(ApplicationContext applicationContext) throws // look for a ControlFlowConfigurator in the application context, if none is found, use the // default one configurator = GeoServerExtensions.bean(ControlFlowConfigurator.class, applicationContext); - if (configurator == null) + if (configurator == null) { configurator = new DefaultControlFlowConfigurator(); + } + checkConfiguration(); + if(controllers.size() == 0) { + LOGGER.info("Control-flow inactive, there are no configured rules"); + } } } diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/config/DefaultControlFlowConfigurator.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/config/DefaultControlFlowConfigurator.java index 61932933564..8934178bb54 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/config/DefaultControlFlowConfigurator.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/config/DefaultControlFlowConfigurator.java @@ -57,6 +57,8 @@ public List buildFlowControllers() throws Exception { for (Object okey : p.keySet()) { String key = ((String) okey).trim(); String value = (String) p.get(okey); + LOGGER.info("Loading control-flow configuration: " + key + "=" + value); + String[] keys = key.trim().split("\\s*\\.\\s*"); int queueSize = 0; diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java index a6ae169201e..f7de0b21aea 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java @@ -46,9 +46,11 @@ public boolean requestIncoming(Request request, long timeout) { public void requestComplete(Request request) { String queueId = QUEUE_ID.get(); QUEUE_ID.remove(); - BlockingQueue queue = queues.get(queueId); - if (queue != null) - queue.remove(request); + if(queueId != null) { + BlockingQueue queue = queues.get(queueId); + if (queue != null) + queue.remove(request); + } } @Override From 64b70458d2a1800eaeae522004ea0ee211c32817 Mon Sep 17 00:00:00 2001 From: aaime Date: Wed, 1 Aug 2012 09:18:36 +0200 Subject: [PATCH 04/72] [GEOS-5243] Using both user and ip flow controllers leads to ip users being locked out --- .../geoserver/flow/ControlFlowCallback.java | 20 ++--- .../flow/controller/IpFlowController.java | 17 +++++ .../flow/controller/QueueController.java | 17 ----- .../flow/controller/UserFlowController.java | 18 ++++- .../FlowControllerTestingThread.java | 20 +++-- .../controller/GlobalFlowControllerTest.java | 20 ++--- .../flow/controller/IpFlowControllerTest.java | 73 ++++++++++++++++--- .../SingleIpFlowControllerTest.java | 24 +++--- .../controller/UserFlowControllerTest.java | 12 +-- 9 files changed, 147 insertions(+), 74 deletions(-) diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java index d4081333a6d..95fc775c1db 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/ControlFlowCallback.java @@ -62,6 +62,16 @@ public void finished(Request request) { } // clean up the thread local REQUEST_CONTROLLERS.remove(); + + // provide some info + if(LOGGER.isLoggable(Level.INFO)) { + if(controllers.size() > 0) { + LOGGER.info("Running requests: " + runningRequests.get() + + ", processing through flow controllers: " + blockedRequests.get()); + } else { + LOGGER.info("Control flow installed, but no rules configured in controlflow.properties"); + } + } } SENTINEL.stop(); } @@ -93,16 +103,6 @@ public Operation operationDispatched(Request request, Operation operation) { } finally { blockedRequests.decrementAndGet(); runningRequests.incrementAndGet(); - - // provide some info - if(LOGGER.isLoggable(Level.INFO)) { - if(controllers.size() > 0) { - LOGGER.info("Running requests: " + runningRequests.get() - + ", processing through flow controllers: " + blockedRequests.get()); - } else { - LOGGER.info("Control flow installed, but no rules configured in controlflow.properties"); - } - } } } return operation; diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/IpFlowController.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/IpFlowController.java index 74d7d92fa08..b85455e1a69 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/IpFlowController.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/IpFlowController.java @@ -5,6 +5,7 @@ package org.geoserver.flow.controller; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -21,6 +22,11 @@ */ public class IpFlowController extends QueueController { + + /** + * Thread local holding the current request queue id TODO: consider having a user map in {@link Request} instead + */ + static ThreadLocal QUEUE_ID = new ThreadLocal(); /** * A flow controller that throttles concurrent requests made from the same ip (any ip) @@ -33,6 +39,17 @@ public class IpFlowController extends QueueController { public IpFlowController(int queueSize) { this.queueSize = queueSize; } + + @Override + public void requestComplete(Request request) { + String queueId = QUEUE_ID.get(); + QUEUE_ID.remove(); + if(queueId != null) { + BlockingQueue queue = queues.get(queueId); + if (queue != null) + queue.remove(request); + } + } @Override public boolean requestIncoming(Request request, long timeout) { diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java index f7de0b21aea..a0b13212ee0 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/QueueController.java @@ -21,12 +21,6 @@ */ public abstract class QueueController implements FlowController { - - /** - * Thread local holding the current request queue id TODO: consider having a user map in {@link Request} instead - */ - static ThreadLocal QUEUE_ID = new ThreadLocal(); - /** * The size of each queue */ @@ -42,17 +36,6 @@ public boolean requestIncoming(Request request, long timeout) { return false; } - @Override - public void requestComplete(Request request) { - String queueId = QUEUE_ID.get(); - QUEUE_ID.remove(); - if(queueId != null) { - BlockingQueue queue = queues.get(queueId); - if (queue != null) - queue.remove(request); - } - } - @Override public int getPriority() { return queueSize; diff --git a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/UserFlowController.java b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/UserFlowController.java index acb3712b53d..9c572c120cf 100644 --- a/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/UserFlowController.java +++ b/src/extension/control-flow/src/main/java/org/geoserver/flow/controller/UserFlowController.java @@ -1,6 +1,7 @@ package org.geoserver.flow.controller; import java.rmi.server.UID; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,7 +29,11 @@ public class UserFlowController extends QueueController { static String COOKIE_NAME = "GS_FLOW_CONTROL"; static String COOKIE_PREFIX = "GS_CFLOW_"; - + + /** + * Thread local holding the current request queue id TODO: consider having a user map in {@link Request} instead + */ + static ThreadLocal QUEUE_ID = new ThreadLocal(); /** * Last time we've performed a queue cleanup @@ -73,6 +78,17 @@ public UserFlowController(int queueSize, int maxQueues, int maxAge) { this.maxQueues = maxQueues; this.maxAge = maxAge; } + + @Override + public void requestComplete(Request request) { + String queueId = QUEUE_ID.get(); + QUEUE_ID.remove(); + if(queueId != null) { + BlockingQueue queue = queues.get(queueId); + if (queue != null) + queue.remove(request); + } + } public boolean requestIncoming(Request request, long timeout) { boolean retval = true; diff --git a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/FlowControllerTestingThread.java b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/FlowControllerTestingThread.java index 15d57f46403..5d4bfff4155 100644 --- a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/FlowControllerTestingThread.java +++ b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/FlowControllerTestingThread.java @@ -6,7 +6,7 @@ public class FlowControllerTestingThread extends Thread { enum ThreadState {STARTED, TIMED_OUT, PROCESSING, COMPLETE}; - FlowController controller; + FlowController[] controllers; boolean proceed; Request request; long timeout; @@ -15,21 +15,23 @@ enum ThreadState {STARTED, TIMED_OUT, PROCESSING, COMPLETE}; Throwable error; - public FlowControllerTestingThread(FlowController controller, Request request, long timeout, long processingDelay) { - this.controller = controller; + public FlowControllerTestingThread(Request request, long timeout, long processingDelay, FlowController... controllers) { + this.controllers = controllers; this.request = request; this.timeout = timeout; this.processingDelay = processingDelay; } - + @Override public void run() { state = ThreadState.STARTED; try { System.out.println(this + " calling requestIncoming"); - if(!controller.requestIncoming(request, timeout)) { - state = ThreadState.TIMED_OUT; - return; + for (FlowController controller : controllers) { + if(!controller.requestIncoming(request, timeout)) { + state = ThreadState.TIMED_OUT; + return; + } } } catch(Throwable t) { this.error = t; @@ -47,7 +49,9 @@ public void run() { try { System.out.println(this + " calling requestComplete"); - controller.requestComplete(request); + for (FlowController controller : controllers) { + controller.requestComplete(request); + } } catch(Throwable t) { this.error = t; } diff --git a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/GlobalFlowControllerTest.java b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/GlobalFlowControllerTest.java index d50bd5a1478..b3b653e07da 100644 --- a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/GlobalFlowControllerTest.java +++ b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/GlobalFlowControllerTest.java @@ -18,12 +18,12 @@ public void testSingleDelay() throws Exception { GlobalFlowController controller = new GlobalFlowController(1); // make three testing threads that will "process" forever, until we interrupt them - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, new Request(), - 0, Long.MAX_VALUE); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, new Request(), - 0, Long.MAX_VALUE); - FlowControllerTestingThread t3 = new FlowControllerTestingThread(controller, new Request(), - 0, Long.MAX_VALUE); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(new Request(), 0, + Long.MAX_VALUE, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(new Request(), 0, + Long.MAX_VALUE, controller); + FlowControllerTestingThread t3 = new FlowControllerTestingThread(new Request(), 0, + Long.MAX_VALUE, controller); try { // start threads making sure every one of them managed to block somewhere before // starting the next one @@ -69,10 +69,10 @@ public void testTimeout() { // make two testing threads that will "process" for 400ms, but with a timeout of 200 on the // flow controller - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, new Request(), - 100, 400); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, new Request(), - 100, 400); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(new Request(), 100, + 400, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(new Request(), 100, + 400, controller); // start t1 first, let go t2 after try { diff --git a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/IpFlowControllerTest.java b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/IpFlowControllerTest.java index 0f478c2ddf0..5fa0a6f07f1 100644 --- a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/IpFlowControllerTest.java +++ b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/IpFlowControllerTest.java @@ -15,8 +15,8 @@ public void testConcurrentRequestsSingleIPAddress() { IpFlowController controller = new IpFlowController(1); String ipAddress = "127.0.0.1"; Request firstRequest = buildRequest(ipAddress, ""); - FlowControllerTestingThread tSample = new FlowControllerTestingThread(controller, - firstRequest, 0, 0); + FlowControllerTestingThread tSample = new FlowControllerTestingThread(firstRequest, + 0, 0, controller); tSample.start(); waitTerminated(tSample, MAX_WAIT); @@ -26,10 +26,10 @@ public void testConcurrentRequestsSingleIPAddress() { // make three testing threads that will "process" forever, and will use the ip to identify themselves // as the same client, until we interrupt them - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, buildRequest( - ip, ""), 0, Long.MAX_VALUE); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, buildRequest( - ip, ""), 0, Long.MAX_VALUE); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, controller); try { // start threads making sure every one of them managed to block somewhere before @@ -58,6 +58,59 @@ public void testConcurrentRequestsSingleIPAddress() { } } + + public void testUserAndIPAddressFlowControl() { + // an ip based flow controller that will allow just one request at a time + IpFlowController ipController = new IpFlowController(1); + UserFlowController userController = new UserFlowController(1); + String ipAddress = "127.0.0.1"; + Request firstRequest = buildRequest(ipAddress, ""); + FlowControllerTestingThread tSample = new FlowControllerTestingThread(firstRequest, + 0, 0, userController, ipController); + tSample.start(); + waitTerminated(tSample, MAX_WAIT); + + assertEquals(ThreadState.COMPLETE, tSample.state); + + String ip = firstRequest.getHttpRequest().getRemoteAddr(); + + // make three testing threads that will "process" forever, and will use the ip to identify themselves + // as the same client, until we interrupt them + FlowControllerTestingThread t1 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, ipController); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, ipController); + + try { + // start threads making sure every one of them managed to block somewhere before + // starting the next one + t1.start(); + waitBlocked(t1, MAX_WAIT); + t2.start(); + waitBlocked(t2, MAX_WAIT); + + assertEquals(ThreadState.PROCESSING, t1.state); + assertEquals(ThreadState.STARTED, t2.state); + + // let t1 go and wait until its termination. This should allow t2 to go + t1.interrupt(); + waitTerminated(t1, MAX_WAIT); + + assertEquals(ThreadState.COMPLETE, t1.state); + assertEquals(ThreadState.PROCESSING, t2.state); + + t2.interrupt(); + waitTerminated(t2, MAX_WAIT); + assertEquals(ThreadState.COMPLETE, t2.state); + } catch (Exception e) { + System.out.println(e.getMessage()); + } finally { + waitAndKill(t1, MAX_WAIT); + waitAndKill(t2, MAX_WAIT); + } + + } + // Test 2 remote addresses that are reported as the same, but have gone through a proxy. These two should not queue up public void testConcurrentProxiedIPAddresses() { @@ -68,10 +121,10 @@ public void testConcurrentProxiedIPAddresses() { String ip = firstRequest.getHttpRequest().getRemoteAddr(); - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, buildRequest( - ip, "192.168.1.2"), 0, Long.MAX_VALUE); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, buildRequest( - ip, "192.168.1.3"), 0, Long.MAX_VALUE); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(buildRequest( + ip, "192.168.1.2"), 0, Long.MAX_VALUE, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(buildRequest( + ip, "192.168.1.3"), 0, Long.MAX_VALUE, controller); try { // start threads making sure every one of them managed to block somewhere before diff --git a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/SingleIpFlowControllerTest.java b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/SingleIpFlowControllerTest.java index 9eedcff032e..643e3118422 100644 --- a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/SingleIpFlowControllerTest.java +++ b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/SingleIpFlowControllerTest.java @@ -13,8 +13,8 @@ public void testConcurrentRequestsSingleIPAddress() { SingleIpFlowController controller = new SingleIpFlowController(1, "127.0.0.1"); String ipAddress = "127.0.0.1"; Request firstRequest = buildRequest(ipAddress, ""); - FlowControllerTestingThread tSample = new FlowControllerTestingThread(controller, - firstRequest, 0, 0); + FlowControllerTestingThread tSample = new FlowControllerTestingThread(firstRequest, + 0, 0, controller); tSample.start(); waitTerminated(tSample, MAX_WAIT); @@ -24,10 +24,10 @@ public void testConcurrentRequestsSingleIPAddress() { // make three testing threads that will "process" forever, and will use the ip to identify themselves // as the same client, until we interrupt them - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, buildRequest( - ip, ""), 0, Long.MAX_VALUE); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, buildRequest( - ip, ""), 0, Long.MAX_VALUE); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, controller); try { // start threads making sure every one of them managed to block somewhere before @@ -60,8 +60,8 @@ public void testConcurrentRequestsDifferentIPAddress() { SingleIpFlowController controller = new SingleIpFlowController(1, "192.168.1.8"); String ipAddress = "127.0.0.1"; Request firstRequest = buildRequest(ipAddress, ""); - FlowControllerTestingThread tSample = new FlowControllerTestingThread(controller, - firstRequest, 0, 0); + FlowControllerTestingThread tSample = new FlowControllerTestingThread(firstRequest, + 0, 0, controller); tSample.start(); waitTerminated(tSample, MAX_WAIT); @@ -69,10 +69,10 @@ public void testConcurrentRequestsDifferentIPAddress() { String ip = firstRequest.getHttpRequest().getRemoteAddr(); - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, buildRequest( - ip, ""), 0, Long.MAX_VALUE); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, buildRequest( - ip, ""), 0, Long.MAX_VALUE); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(buildRequest( + ip, ""), 0, Long.MAX_VALUE, controller); try { // start threads making sure every one of them managed to block somewhere before diff --git a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/UserFlowControllerTest.java b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/UserFlowControllerTest.java index 5acba038215..e31fa6b1698 100644 --- a/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/UserFlowControllerTest.java +++ b/src/extension/control-flow/src/test/java/org/geoserver/flow/controller/UserFlowControllerTest.java @@ -16,8 +16,8 @@ public void testConcurrentRequestsSingleUser() { UserFlowController controller = new UserFlowController(1); Request firstRequest = buildRequest(null); - FlowControllerTestingThread tSample = new FlowControllerTestingThread(controller, firstRequest, - 0, 0); + FlowControllerTestingThread tSample = new FlowControllerTestingThread(firstRequest, 0, + 0, controller); tSample.start(); waitTerminated(tSample, MAX_WAIT); @@ -26,10 +26,10 @@ public void testConcurrentRequestsSingleUser() { // make three testing threads that will "process" forever, and will use the cookie to identify themselves // as the same client, until we interrupt them - FlowControllerTestingThread t1 = new FlowControllerTestingThread(controller, buildRequest(cookieValue), - 0, Long.MAX_VALUE); - FlowControllerTestingThread t2 = new FlowControllerTestingThread(controller, buildRequest(cookieValue), - 0, Long.MAX_VALUE); + FlowControllerTestingThread t1 = new FlowControllerTestingThread(buildRequest(cookieValue), 0, + Long.MAX_VALUE, controller); + FlowControllerTestingThread t2 = new FlowControllerTestingThread(buildRequest(cookieValue), 0, + Long.MAX_VALUE, controller); try { // start threads making sure every one of them managed to block somewhere before // starting the next one From 5691e91c16cd0abe14aa11b3245337a8b358a394 Mon Sep 17 00:00:00 2001 From: David Winslow Date: Wed, 1 Aug 2012 10:24:30 -0400 Subject: [PATCH 05/72] Correct typo in registration bean for RememberMeAuthFilterPanel --- src/web/security/src/main/java/applicationContext.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/security/src/main/java/applicationContext.xml b/src/web/security/src/main/java/applicationContext.xml index 7fe1e3ffd81..02d7af243c4 100644 --- a/src/web/security/src/main/java/applicationContext.xml +++ b/src/web/security/src/main/java/applicationContext.xml @@ -104,7 +104,7 @@ - + From 704bb345f01f1f6c4b68ecc4b6d18471167902b8 Mon Sep 17 00:00:00 2001 From: David Winslow Date: Wed, 1 Aug 2012 10:26:58 -0400 Subject: [PATCH 06/72] Tighten type constraints on AuthenticationFilterPanel[Info] Closes GEOS-5241 --- .../geoserver/security/web/auth/AuthenticationFilterPanel.java | 3 ++- .../security/web/auth/AuthenticationFilterPanelInfo.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanel.java b/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanel.java index dbf18571a3b..f306531c899 100644 --- a/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanel.java +++ b/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanel.java @@ -6,6 +6,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; +import org.geoserver.security.config.SecurityAuthFilterConfig; import org.geoserver.security.config.SecurityFilterConfig; import org.geoserver.security.web.SecurityNamedServicePanel; @@ -15,7 +16,7 @@ * @author Justin Deoliveira, OpenGeo * */ -public class AuthenticationFilterPanel +public class AuthenticationFilterPanel extends SecurityNamedServicePanel { public AuthenticationFilterPanel(String id, IModel model) { diff --git a/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanelInfo.java b/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanelInfo.java index 5a6b7283374..f7efc4bb698 100644 --- a/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanelInfo.java +++ b/src/web/security/src/main/java/org/geoserver/security/web/auth/AuthenticationFilterPanelInfo.java @@ -4,6 +4,7 @@ */ package org.geoserver.security.web.auth; +import org.geoserver.security.config.SecurityAuthFilterConfig; import org.geoserver.security.config.SecurityFilterConfig; import org.geoserver.security.web.SecurityNamedServicePanelInfo; @@ -14,7 +15,7 @@ * */ public class AuthenticationFilterPanelInfo - > + > extends SecurityNamedServicePanelInfo{ } From 8eb7011619bdb78c8bca2e35156be9b11bddac6c Mon Sep 17 00:00:00 2001 From: Ian Schneider Date: Wed, 1 Aug 2012 13:04:32 -0600 Subject: [PATCH 07/72] make wms describeLayer strip prefix for workspace when using a workspace virtual service, remove the prefix of the layer name add tests --- .../DescribeLayerTransformer.java | 2 +- .../wms/wms_1_1_1/LayerWorkspaceTest.java | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/LayerWorkspaceTest.java diff --git a/src/wms/src/main/java/org/geoserver/wms/describelayer/DescribeLayerTransformer.java b/src/wms/src/main/java/org/geoserver/wms/describelayer/DescribeLayerTransformer.java index bbb4e3f2e20..0c45984c70c 100644 --- a/src/wms/src/main/java/org/geoserver/wms/describelayer/DescribeLayerTransformer.java +++ b/src/wms/src/main/java/org/geoserver/wms/describelayer/DescribeLayerTransformer.java @@ -173,7 +173,7 @@ private void handleLayers(DescribeLayerRequest req) { layerAtts.addAttribute("", "owsType", "owsType", "", owsType); } - layerAtts.setAttribute(0, "", "name", "name", "", layer.getName()); + layerAtts.setAttribute(0, "", "name", "name", "", layer.getLayerInfo().prefixedName()); start("LayerDescription", layerAtts); queryAtts.setAttribute(0, "", "typeName", "typeName", "", layer.getName()); diff --git a/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/LayerWorkspaceTest.java b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/LayerWorkspaceTest.java new file mode 100644 index 00000000000..430e57574ea --- /dev/null +++ b/src/wms/src/test/java/org/geoserver/wms/wms_1_1_1/LayerWorkspaceTest.java @@ -0,0 +1,50 @@ +package org.geoserver.wms.wms_1_1_1; + +import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists; + +import javax.xml.namespace.QName; + +import org.geoserver.catalog.Catalog; +import org.geoserver.catalog.LayerInfo; +import org.geoserver.data.test.MockData; +import org.geoserver.wms.WMSTestSupport; +import org.w3c.dom.Document; + +public class LayerWorkspaceTest extends WMSTestSupport{ + + private Catalog catalog; + + @Override + protected void oneTimeSetUp() throws Exception { + super.oneTimeSetUp(); + catalog = getCatalog(); + } + + LayerInfo layer(Catalog cat, QName name) { + return cat.getLayerByName(getLayerId(name)); + } + + public void testGlobalCapabilities() throws Exception { + LayerInfo layer = layer(catalog, MockData.PRIMITIVEGEOFEATURE); + Document doc = getAsDOM("/wms?service=WMS&request=getCapabilities&version=1.1.1", true); + assertXpathExists("//Layer[Name='" + layer.prefixedName() + "']", doc); + } + + public void testGlobalDescribeLayer() throws Exception { + LayerInfo layer = layer(catalog, MockData.PRIMITIVEGEOFEATURE); + Document doc = getAsDOM("/wms?service=WMS&request=describeLayer&version=1.1.1&LAYERS=" + + layer.getName(), true); + assertXpathExists("//LayerDescription[@name='" + layer.prefixedName() + "']", doc); + } + + public void testWorkspaceCapabilities() throws Exception { + Document doc = getAsDOM("/sf/wms?service=WMS&request=getCapabilities&version=1.1.1", true); + assertXpathExists("//Layer[Name='" + MockData.PRIMITIVEGEOFEATURE.getLocalPart()+ "']", doc); + } + + public void testWorkspaceDescribeLayer() throws Exception { + Document doc = getAsDOM("/sf/wms?service=WMS&request=describeLayer&version=1.1.1&LAYERS=" + + MockData.PRIMITIVEGEOFEATURE.getLocalPart(), true); + assertXpathExists("//LayerDescription[@name='" + MockData.PRIMITIVEGEOFEATURE.getLocalPart() + "']", doc); + } +} From d62dd7924c8544327e0613480fc4d2464849d8e0 Mon Sep 17 00:00:00 2001 From: Gabriel Roldan Date: Wed, 1 Aug 2012 17:45:11 -0300 Subject: [PATCH 08/72] use a case insensitive map for GetMapRequest's httpRequestHeaders. Tomcat lower-cases their names --- src/wms/src/main/java/org/geoserver/wms/GetMapRequest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wms/src/main/java/org/geoserver/wms/GetMapRequest.java b/src/wms/src/main/java/org/geoserver/wms/GetMapRequest.java index ece31a3786d..28db8d0fbf8 100644 --- a/src/wms/src/main/java/org/geoserver/wms/GetMapRequest.java +++ b/src/wms/src/main/java/org/geoserver/wms/GetMapRequest.java @@ -806,9 +806,10 @@ public String getHttpRequestHeader(String headerName) { return httpRequestHeaders == null ? null : httpRequestHeaders.get(headerName); } + @SuppressWarnings("unchecked") public void putHttpRequestHeader(String headerName, String value) { if (httpRequestHeaders == null) { - httpRequestHeaders = new HashMap(); + httpRequestHeaders = new CaseInsensitiveMap(new HashMap()); } httpRequestHeaders.put(headerName, value); } From 228a72b2ef4f1fd2984b37073a081334f44277d9 Mon Sep 17 00:00:00 2001 From: Gabriel Roldan Date: Wed, 1 Aug 2012 17:56:03 -0300 Subject: [PATCH 09/72] lower logging severity of geoserver tile layer loading --- .../java/org/geoserver/gwc/layer/DefaultTileLayerCatalog.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gwc/src/main/java/org/geoserver/gwc/layer/DefaultTileLayerCatalog.java b/src/gwc/src/main/java/org/geoserver/gwc/layer/DefaultTileLayerCatalog.java index 8c15cbd945f..25f2f726fad 100644 --- a/src/gwc/src/main/java/org/geoserver/gwc/layer/DefaultTileLayerCatalog.java +++ b/src/gwc/src/main/java/org/geoserver/gwc/layer/DefaultTileLayerCatalog.java @@ -288,7 +288,9 @@ private File getFile(final String tileLayerId, final boolean create) throws IOEx } private GeoServerTileLayerInfoImpl depersist(final File file) throws IOException { - LOGGER.info("Depersisting GeoServerTileLayerInfo from " + file.getAbsolutePath()); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Depersisting GeoServerTileLayerInfo from " + file.getAbsolutePath()); + } GeoServerTileLayerInfoImpl info; Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8"); try { From 61defcb55303b5067860c1583553382e78ef0f0c Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Thu, 2 Aug 2012 15:07:35 -0700 Subject: [PATCH 10/72] Updates and edits to GWC REST docs --- .../source/geowebcache/rest/diskquota.rst | 250 ++++++------- doc/en/user/source/geowebcache/rest/index.rst | 19 +- .../user/source/geowebcache/rest/layers.rst | 348 +++++++++--------- doc/en/user/source/geowebcache/rest/seed.rst | 336 ++++++++--------- 4 files changed, 438 insertions(+), 515 deletions(-) diff --git a/doc/en/user/source/geowebcache/rest/diskquota.rst b/doc/en/user/source/geowebcache/rest/diskquota.rst index 47350f04bac..47be66bc4de 100644 --- a/doc/en/user/source/geowebcache/rest/diskquota.rst +++ b/doc/en/user/source/geowebcache/rest/diskquota.rst @@ -1,15 +1,14 @@ -.. _rest.diskquota: +.. _gwc_rest_diskquota: -Disk Quota REST API -=================== +Disk Quota +========== -The REST API for Disk Quota management provides a RESTful interface through which clients can -configure the disk usage limits and expiration policies for a GeoWebCache instance through simple HTTP calls. +The GeoWebCache REST API provides a RESTful interface through which clients can configure the disk usage limits and expiration policies for a GeoWebCache instance. Operations ---------- -``/gwc/rest/diskquota.`` +URL: ``/gwc/rest/diskquota.`` .. list-table:: :header-rows: 1 @@ -35,150 +34,87 @@ Operations - 405 - -*Representations*: +**Representations**: -- :download:`XML ` -- :download:`JSON ` +* :download:`XML ` +* :download:`JSON ` +The examples below use the `cURL `_ utility, though the examples apply to any HTTP-capable tool or library. -Disk quota cURL Examples ------------------------- +Retrieving the current configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The examples in this section use the `cURL `_ -utility, which is a handy command line tool for executing HTTP requests and -transferring files. Though cURL is used the examples apply to any HTTP-capable -tool or library. +The following obtains the current disk quota configuration in **XML** format: -Getting the current Disk Quota configuration -++++++++++++++++++++++++++++++++++++++++++++ - -The following obtains the current disk quota configuration in XML format: - -.. code-block:: xml +.. code-block:: console curl -u admin:geoserver -v -XGET http://localhost:8080/geoserver/gwc/rest/diskquota.xml -The response should look like: - -.. code-block:: xml - - < HTTP/1.1 200 OK - < Date: Mon, 21 Mar 2011 13:50:49 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Content-Type: text/xml; charset=ISO-8859-1 - < Content-Length: 422 - < - - true - 2048 - 5 - SECONDS - 5 - LRU - - 100 - MiB - - - - -The following obtains the current disk quota configuration in JSON format: +:: + + < HTTP/1.1 200 OK + < Date: Mon, 21 Mar 2011 13:50:49 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Content-Type: text/xml; charset=ISO-8859-1 + < Content-Length: 422 + < + + true + 2048 + 5 + SECONDS + 5 + LRU + + 100 + MiB + + + + +The following obtains the current disk quota configuration in **JSON** format: .. code-block:: xml curl -u admin:geoserver -v -XGET http://localhost:8080/geoserver/gwc/rest/diskquota.json -The response should look like: - -.. code-block:: xml - - < HTTP/1.1 200 OK - < Date: Mon, 21 Mar 2011 13:53:42 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Content-Type: application/json; charset=ISO-8859-1 - < Content-Length: 241 - < - * Connection #0 to host localhost left intact - * Closing connection #0 - {"gwcQuotaConfiguration":{"diskBlockSize":2048,"enabled":true,"maxConcurrentCleanUps":5,"cacheCleanUpFrequency":5,"globalExpirationPolicyName":"LRU","globalQuota":{"value":"100","units":"MiB"},"cacheCleanUpUnits":"SECONDS"}} - - -Changing Disk Quota configuration -+++++++++++++++++++++++++++++++++ - -Request body for PUT can contain only the desired properties to be modified, and a diff will be applied to the current configuration. For example: +:: -The following will only change the maxConcurrentCleanups property in XML format: + < HTTP/1.1 200 OK + < Date: Mon, 21 Mar 2011 13:53:42 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Content-Type: application/json; charset=ISO-8859-1 + < Content-Length: 241 + < + * Connection #0 to host localhost left intact + * Closing connection #0 + {"gwcQuotaConfiguration":{"diskBlockSize":2048,"enabled":true,"maxConcurrentCleanUps":5,"cacheCleanUpFrequency":5,"globalExpirationPolicyName":"LRU","globalQuota":{"value":"100","units":"MiB"},"cacheCleanUpUnits":"SECONDS"}} -.. code-block:: xml - - 2 - -The following will only change the diskBlockSize, enabled, and globalQuota properties in JSON format: - -.. code-block:: xml - - {"gwcQuotaConfiguration":{"diskBlockSize":2048,"enabled":true,"globalQuota":{"value":"100","units":"MiB"}} - -(valid values for "units" are ) - -Invalid XML request: -^^^^^^^^^^^^^^^^^^^^ -Invalid parameter (here maxConcurrentCleanUps must be > 0) produce a 400 response code and contains the error message as plain text: -.. code-block:: xml +Changing configuration +~~~~~~~~~~~~~~~~~~~~~~ - curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.xml" -X PUT -d "-1" +.. note:: -.. code-block:: xml + The request body for PUT should contain only the desired properties to be modified. For example, the following will only change the maxConcurrentCleanups property in XML format: - < HTTP/1.1 400 Bad Request - < Date: Fri, 18 Mar 2011 20:53:26 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Content-Type: text/plain; charset=ISO-8859-1 - < Content-Length: 53 - < - * Connection #0 to host localhost left intact - * Closing connection #0 - maxConcurrentCleanUps shall be a positive integer: -1 + .. code-block:: xml -Invalid JSON request: -^^^^^^^^^^^^^^^^^^^^^ + 2 -.. code-block:: xml + The following will only change the diskBlockSize, enabled, and globalQuota properties in JSON format: - curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.json" -X PUT -d "{"gwcQuotaConfiguration":{"globalQuota":{"value":"100","units":"ZZiB"}}}" + .. code-block:: json -.. code-block:: xml + {"gwcQuotaConfiguration":{"diskBlockSize":2048,"enabled":true,"globalQuota":{"value":"100","units":"MiB"}} - < HTTP/1.1 400 Bad Request - < Date: Fri, 18 Mar 2011 20:56:23 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Content-Type: text/plain; charset=ISO-8859-1 - < Content-Length: 601 - < - No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB : No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB - ---- Debugging information ---- - message : No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB - cause-exception : java.lang.IllegalArgumentException - cause-message : No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB - class : org.geowebcache.diskquota.DiskQuotaConfig - required-type : org.geowebcache.diskquota.storage.Quota - line number : -1 - * Connection #0 to host localhost left intact - * Closing connection #0 - -Valid XML requests: -^^^^^^^^^^^^^^^^^^^ -(note upon successfully applying the changes the full config in the given format is returned) - -Change enabled and globalQuota in XML format: +The following XML example successfully enables the quota and sets the globalQuota size: -.. code-block:: xml +.. code-block:: console curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.xml" -X PUT -d "true100GiB" -.. code-block:: xml +:: < HTTP/1.1 200 OK < Date: Fri, 18 Mar 2011 20:59:31 GMT @@ -200,24 +136,64 @@ Change enabled and globalQuota in XML format: -Valid JSON request: -^^^^^^^^^^^^^^^^^^^ -Change globalQuota and expirationPolicyName in JSON format: +The following JSON example changes the globalQuote and expirationPolicyName -.. code-block:: xml +.. code-block:: console - curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.json" -X PUT -d "{"gwcQuotaConfiguration":{"globalQuota":{"value":"100","units":"MiB"},"globalExpirationPolicyName":"LRU"}}" + curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.json" -X PUT -d "{"gwcQuotaConfiguration":{"globalQuota":{"value":"100","units":"MiB"},"globalExpirationPolicyName":"LRU"}}" -.. code-block:: xml +:: - < HTTP/1.1 200 OK - < Date: Fri, 18 Mar 2011 21:02:20 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Content-Type: application/json; charset=ISO-8859-1 - < Content-Length: 241 - < - * Connection #0 to host localhost left intact - * Closing connection #0 - {"gwcQuotaConfiguration":{"diskBlockSize":2048,"enabled":true,"maxConcurrentCleanUps":5,"cacheCleanUpFrequency":5,"globalExpirationPolicyName":"LRU","globalQuota":{"value":"100","units":"MiB"},"cacheCleanUpUnits":"SECONDS","layerQuotas":[]}} + < HTTP/1.1 200 OK + < Date: Fri, 18 Mar 2011 21:02:20 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Content-Type: application/json; charset=ISO-8859-1 + < Content-Length: 241 + < + * Connection #0 to host localhost left intact + * Closing connection #0 + {"gwcQuotaConfiguration":{"diskBlockSize":2048,"enabled":true,"maxConcurrentCleanUps":5,"cacheCleanUpFrequency":5,"globalExpirationPolicyName":"LRU","globalQuota":{"value":"100","units":"MiB"},"cacheCleanUpUnits":"SECONDS","layerQuotas":[]}} + + +The following *invalid* XML example has an invalid parameter (maxConcurrentCleanUps must be > 0). It returns a 400 response code and contains an error message as plain text: + +.. code-block:: console + + curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.xml" -X PUT -d "-1" + +:: + + < HTTP/1.1 400 Bad Request + < Date: Fri, 18 Mar 2011 20:53:26 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Content-Type: text/plain; charset=ISO-8859-1 + < Content-Length: 53 + < + * Connection #0 to host localhost left intact + * Closing connection #0 + maxConcurrentCleanUps shall be a positive integer: -1 + +The following *invalid* JSON example uses an unknown unit of measure (ZZiB). It returns a 400 response code and contains an error message as plain text: + +.. code-block:: console + + curl -v -u admin:geoserver "http://localhost:8090/geoserver/gwc/rest/diskquota.json" -X PUT -d "{"gwcQuotaConfiguration":{"globalQuota":{"value":"100","units":"ZZiB"}}}" +:: + < HTTP/1.1 400 Bad Request + < Date: Fri, 18 Mar 2011 20:56:23 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Content-Type: text/plain; charset=ISO-8859-1 + < Content-Length: 601 + < + No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB : No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB + ---- Debugging information ---- + message : No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB + cause-exception : java.lang.IllegalArgumentException + cause-message : No enum const class org.geowebcache.diskquota.storage.StorageUnit.ZZiB + class : org.geowebcache.diskquota.DiskQuotaConfig + required-type : org.geowebcache.diskquota.storage.Quota + line number : -1 + * Connection #0 to host localhost left intact + * Closing connection #0 diff --git a/doc/en/user/source/geowebcache/rest/index.rst b/doc/en/user/source/geowebcache/rest/index.rst index f0a89863e42..076d3173aae 100644 --- a/doc/en/user/source/geowebcache/rest/index.rst +++ b/doc/en/user/source/geowebcache/rest/index.rst @@ -3,15 +3,20 @@ GeoWebCache REST API ==================== -This section will discuss the GeoWebCache REST API. +This section discusses the GeoWebCache REST API, an interface for working programmatically with the integrated GeoWebCache without the need for a GUI. -Note that for the GeoWebCache version integrated with GeoServer, the GWC REST API base URL is ``/gwc/rest``, whilst -for the standalone GeoWebCache it's jsut ``/rest``. +The GeoWebCache REST endpoint when integrated with GeoServer is available at:: + + /gwc/rest/ + +For example:: + + http://example.com:8080/geoserver/gwc/rest/ .. toctree:: - :maxdepth: 1 + :maxdepth: 2 - layers.rst - seed.rst - diskquota.rst + layers + seed + diskquota diff --git a/doc/en/user/source/geowebcache/rest/layers.rst b/doc/en/user/source/geowebcache/rest/layers.rst index 9b1ba4f1937..35ef45be95f 100644 --- a/doc/en/user/source/geowebcache/rest/layers.rst +++ b/doc/en/user/source/geowebcache/rest/layers.rst @@ -1,15 +1,16 @@ -.. _rest.layers: +.. _gwc_rest_layers: -Managing Layers through the REST API -==================================== +Managing Layers +=============== -The REST API for Layer management provides a RESTful interface through which clients can -programatically add, modify, or remove cached Layers. +The GeoWebCache REST API provides a RESTful interface through which clients can add, modify, or remove cached layers. -Layers list ------------ +.. note:: JSON representation is not recommended as the library used for JSON has issues with multi-valued properties such as "parameterFilters". -``/gwc/rest/seed/layers.xml`` +Layer list +---------- + +URL: ``/gwc/rest/seed/layers.xml`` .. list-table:: :header-rows: 1 @@ -35,37 +36,35 @@ Layers list - 400 - -Note: JSON representation is intentionally left aside as the library used for JSON marshaling has issues with multi-valued properties such as `parameterFilters`. - -Sample request: +The following example will request a full list of layers: .. code-block:: xml - curl -u admin:geoserver "http://localhost:8080/geoserver/gwc/rest/layers" + curl -u admin:geoserver "http://localhost:8080/geoserver/gwc/rest/layers" -Sample response: - .. code-block:: xml - - - img states - - - - raster test layer - - - - topp:states - - - + + + img states + + + + raster test layer + + + + topp:states + + + Layer Operations ---------------- -``/gwc/rest/seed/layers/.xml`` +URL: ``/gwc/rest/seed/layers/.xml`` + +.. note:: JSON representation is not recommended as the library used for JSON has issues with multi-valued properties such as "parameterFilters". .. list-table:: :header-rows: 1 @@ -75,198 +74,179 @@ Layer Operations - Return Code - Formats * - GET - - Return the XML representation of the Layer + - Return the XML representation of the layer - 200 - XML * - POST - - Modify the definition/configuration of a Layer + - Modify the definition/configuration of the layer - 200 - XML * - PUT - - Add a new Layer + - Add a new layer - 200 - XML * - DELETE - - Delete a Layer + - Delete the layer - 200 - -.. note:: Beware that for the GeoServer integrated version of GeoWebcache, there are two different - representations for cached layers, depending on whether the tile layer is created from a GeoServer's - WMS layer or layer group (i.e. a ``GeoServerLayer``), or rather it doesn't match a GeoServer layer, - but is configured in ``geowebcache.xml`` as a regular GWC layer (i.e. a ``wmsLayer``). - The two formats are pretty similar. The main difference being that "integrated" layers are called - GeoServerLayer and contain no image data source information, such as origin WMS URL, since they - match a GeoServer owned layer (or layer group) and hence the image operations are routed to the GeoServer - rendering engine internally (hence being more efficient). +.. note:: There are two different representations for cached layers, depending on whether the tile layer is created from the GeoServer WMS layer or layer group (``GeoServerLayer``), or is configured in ``geowebcache.xml`` as a regular GWC layer (``wmsLayer``). A GeoServer layer is referred to as a ``GeoServerLayer`` and contains no image data source information such as origin WMS URL. -*Representations*: +**Representations**: -- Standalone :download:`XML minimal ` -- Standalone :download:`XML ` -- Integrated :download:`XML minimal ` -- Integrated :download:`XML ` +* GeoWebCache (``wmsLayer``) :download:`XML minimal ` +* GeoWebCache (``wmsLayer``) :download:`XML ` +* GeoServer (``GeoServerLayer``) :download:`XML minimal ` +* GeoServer (``GeoServerLayer``) :download:`XML ` -.. note:: a JSON representation is intentionally left aside as the library used for JSON marshaling has issues with multi-valued properties such as `parameterFilters`. -REST API for Layers, cURL Examples ----------------------------------- +The examples below use the `cURL `_ utility, though the examples apply to any HTTP-capable tool or library. -The examples in this section use the `cURL `_ -utility, which is a handy command line tool for executing HTTP requests and -transferring files. Though cURL is used the examples apply to any HTTP-capable -tool or library. +Adding a GeoWebCache layer +~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add Standalone Layer -++++++++++++++++++++ +The following example will add a new layer to GeoWebCache: -Sample request: +.. code-block:: console -Given a `layer.xml` file as the following: + curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d @layer.xml "http://localhost:8080/geoserver/gwc/rest/layers/newlayer.xml" -.. code-block:: xml +where the :file:`layer.xml` file is defined as the following: - - layer1 - - image/png - - - - EPSG:900913 - - - - http://localhost:8080/geoserver/wms - - topp:states - +.. code-block:: xml -.. code-block:: xml + + newlayer + + image/png + + + + EPSG:900913 + + + + http://localhost:8080/geoserver/wms + + topp:states + - curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d @layer.xml "http://localhost:8080/geoserver/gwc/rest/layers/layer1.xml" +.. note:: The addressed resource ``newlayer.xml``, without the ``.xml`` extension, must match the name of the layer in the XML representation. -.. note:: the addressed resource ``layer1.xml``, without the ``.xml`` extension, must match the name of the layer in the xml representation. +Adding a GeoServer layer +~~~~~~~~~~~~~~~~~~~~~~~~ +The following example will add a new layer to both GeoServer and GeoWebCache: +.. code-block:: console -Add Integrated Layer -++++++++++++++++++++ + curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d @poi.xml "http://localhost:8080/geoserver/gwc/rest/layers/tiger:poi.xml" -Given a `poi.xml` file as the following: +where the :file:`poi.xml` file is defined as the following: .. code-block:: xml - - LayerInfoImpl--570ae188:124761b8d78:-7fd0 - true - tiger:poi - - image/png8 - - - - GoogleCRS84Quad - 0 - 14 - 1 - 9 - - - - 4 - 4 - - 50 - true - - -.. code-block:: xml - - curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d @poi.xml "http://localhost:8080/geoserver/gwc/rest/layers/tiger:poi.xml" - -.. note:: the addressed resource ``tiger:poi.xml``, without the ``poi.xml`` extension, must match the name of the layer in the xml representation, - as well as the name of an _existing_ GeoServer layer or layer group. - -Modify Layer -++++++++++++ - -Now, make some modifications to the layer definition on the `layer.xml` file: + + LayerInfoImpl--570ae188:124761b8d78:-7fd0 + true + tiger:poi + + image/png8 + + + + GoogleCRS84Quad + 0 + 14 + 1 + 9 + + + + 4 + 4 + + 50 + true + + +.. note:: The addressed resource ``tiger:poi.xml``, without the ``poi.xml`` extension, must match the name of the layer in the XML representation, as well as the name of an *existing* GeoServer layer or layer group. + +Modifying a layer +~~~~~~~~~~~~~~~~~ + +This example modifies the layer definition via the :file:`layer.xml` file. The request adds a parameter filter and a grid subset to the existing ``tiger:poi`` tile layer: .. code-block:: xml - - true - tiger:poi - - image/png8 - - - - GoogleCRS84Quad - 0 - 14 - 1 - 9 - - - EPSG:900913 - - - -8238959.403861314 - 4969300.121476209 - -8237812.689219721 - 4971112.167757057 - - - - - - 4 - 4 - - - - ELEVATION - 0.0 - - 0.0 - 1.0 - 2.0 - 3.0 - 4.0 - - 1.0E-3 - - - 50 - true - - -And use the HTTP POST method instead: - -.. code-block:: xml - - curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d @poi.xml "http://localhost:8080/geoserver/gwc/rest/layers/tiger:poi.xml" - -The above request adds a parameter filter and a grid subset to the existing ``tiger:poi`` tile layer. -Also, note that the ```` element is optional, but if present, both the id and the name attribute must match the correspoinding -id and name properties of the matching GeoServer layer or layer group. - -Delete Layer -++++++++++++ - -Deleting a GeoWebCache tile layer deletes the layer configuration and **its cache on disk**. No tile images will remain on the -cache directory after deleting a tile layer. + + true + tiger:poi + + image/png8 + + + + GoogleCRS84Quad + 0 + 14 + 1 + 9 + + + EPSG:900913 + + + -8238959.403861314 + 4969300.121476209 + -8237812.689219721 + 4971112.167757057 + + + + + + 4 + 4 + + + + ELEVATION + 0.0 + + 0.0 + 1.0 + 2.0 + 3.0 + 4.0 + + 1.0E-3 + + + 50 + true + + +Instead of PUT, use the HTTP POST method instead: + +.. code-block:: console + + curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d @poi.xml "http://localhost:8080/geoserver/gwc/rest/layers/tiger:poi.xml" + + +Deleting a layer +~~~~~~~~~~~~~~~~ + +Deleting a GeoWebCache tile layer deletes the layer configuration *as well as the layer's disk cache*. No tile images will remain on the cache directory after deleting a tile layer. To delete a layer, use the HTTP DELETE method against the layer resource: -.. code-block:: xml +.. code-block:: console + + curl -v -u admin:geoserver -XDELETE "http://localhost:8080/geoserver/gwc/rest/layers/newlayer.xml" + +.. note:: - curl -v -u admin:geoserver -XDELETE "http://localhost:8080/geoserver/gwc/rest/layers/layer1.xml" + If trying to delete a tile layer that is an integrated ``GeoServerLayer``, only the GeoWebCache layer definition will be deleted; the GeoServer definition is left untouched. To delete a layer in GeoServer, you will need to use the GeoServer :ref:`REST API ` to manipulate GeoServer resources. -.. note:: in the case that the tile layer being deleted is an integrated ``GeoServerLayer``, only the GeoWebCache layer is being - deleted, the GeoServer layer or layer group is left untouched. You need to use GeoServer's own REST API to manipulate GeoServer - resources. Buf if instead you're using the GeoServer REST API to delete a GeoServer layer or layer group that *has* a - tile layer associated, this last one will get deleted as a side effect even if you didn't explicitly tried to remove the tile layer. - + On the other hand, deleting a GeoServer layer via the GeoServer REST API *will* automatically delete the associated tile layer. diff --git a/doc/en/user/source/geowebcache/rest/seed.rst b/doc/en/user/source/geowebcache/rest/seed.rst index 0457089f35f..4d55e562ee5 100644 --- a/doc/en/user/source/geowebcache/rest/seed.rst +++ b/doc/en/user/source/geowebcache/rest/seed.rst @@ -1,15 +1,14 @@ -.. _rest.seed: +.. _gwc_rest_seed: -Seeding and truncating through the REST API -=========================================== +Seeding and Truncating +====================== -The REST API for cache seeding and truncation provides a RESTful interface through which clients can -programatically add or remove tiles from the cache, on a layer by layer basis. +The GeoWebCache REST API provides a RESTful interface through which clients can add or remove tiles from the cache on a per-layer basis. Operations ---------- -``/gwc/rest/seed/.`` +URL: ``/gwc/rest/seed/.`` .. list-table:: :header-rows: 1 @@ -35,132 +34,118 @@ Operations - 405 - -*Representations*: +**Representations**: -- :download:`XML ` -- :download:`JSON ` +* :download:`XML ` +* :download:`JSON ` +The examples below use the `cURL `_ utility, though the examples apply to any HTTP-capable tool or library. -Seed/Truncate cURL Examples ---------------------------- +Seeding +~~~~~~~ -The examples in this section use the `cURL `_ -utility, which is a handy command line tool for executing HTTP requests and -transferring files. Though cURL is used the examples apply to any HTTP-capable -tool or library. +The following XML request initiates a seeding task: -Seeding XML example -+++++++++++++++++++ +.. code-block:: console -Sample request: - -.. code-block:: xml - - curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d 'nurc:Arc_Sample4326112image/pngtruncate2' "http://localhost:8080/geoserver/gwc/rest/seed/nurc:Arc_Sample.xml" + curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d 'nurc:Arc_Sample4326112image/pngtruncate2' "http://localhost:8080/geoserver/gwc/rest/seed/nurc:Arc_Sample.xml" -Sample response: - -.. code-block:: xml +:: - * About to connect() to localhost port 8080 (#0) - * Trying 127.0.0.1... connected - * Connected to localhost (127.0.0.1) port 8080 (#0) - * Server auth using Basic with user 'admin' - > POST /geoserver/gwc/rest/seed/nurc:Arc_Sample.xml HTTP/1.1 - > Authorization: Basic YWRtaW46Z2Vvc2VydmVy - > User-Agent: curl/7.21.3 (x86_64-pc-linux-gnu) libcurl/7.21.3 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18 - > Host: localhost:8080 - > Accept: */* - > Content-type: text/xml - > Content-Length: 209 - > - < HTTP/1.1 200 OK + * About to connect() to localhost port 8080 (#0) + * Trying 127.0.0.1... connected + * Connected to localhost (127.0.0.1) port 8080 (#0) + * Server auth using Basic with user 'admin' + > POST /geoserver/gwc/rest/seed/nurc:Arc_Sample.xml HTTP/1.1 + > Authorization: Basic YWRtaW46Z2Vvc2VydmVy + > User-Agent: curl/7.21.3 (x86_64-pc-linux-gnu) libcurl/7.21.3 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18 + > Host: localhost:8080 + > Accept: */* + > Content-type: text/xml + > Content-Length: 209 + > + < HTTP/1.1 200 OK -Here's a more complete xml fragment for a seed request, including parameter filters: +The following is a more complete XML fragment for a seed request, including parameter filters: .. code-block:: xml - - - topp:states - - - -2495667.977678598 - -2223677.196231552 - 3291070.6104286816 - 959189.3312465074 - - - - - EPSG:2163 - 0 - - 2 - image/png + + + topp:states + + + -2495667.977678598 + -2223677.196231552 + 3291070.6104286816 + 959189.3312465074 + + + + + EPSG:2163 + 0 + 2 + image/png - - truncate - - - 1 - - - - STYLES - pophatch - - - CQL_FILTER - TOTPOP > 10000 - - - - - -Truncate JSON example -+++++++++++++++++++++ - -Sample request: - -.. code-block:: xml - - curl -v -u admin:geoserver -XPOST -H "Content-type: application/json" -d "{'seedRequest':{'name':'topp:states','bounds':{'coords':{ 'double':['-124.0','22.0','66.0','72.0']}},'srs':{'number':4326},'zoomStart':1,'zoomStop':12,'format':'image\/png','type':'truncate','threadCount':4}}}" "http://localhost:8080/geoserver/gwc/rest/seed/nurc:Arc_Sample.json" + + truncate + + + 1 + + + + STYLES + pophatch + + + CQL_FILTER + TOTPOP > 10000 + + + + + +Truncating +~~~~~~~~~~ + +The following XML request initiates a seeding task: + +.. code-block:: console + + curl -v -u admin:geoserver -XPOST -H "Content-type: application/json" -d "{'seedRequest':{'name':'topp:states','bounds':{'coords':{ 'double':['-124.0','22.0','66.0','72.0']}},'srs':{'number':4326},'zoomStart':1,'zoomStop':12,'format':'image\/png','type':'truncate','threadCount':4}}}" "http://localhost:8080/geoserver/gwc/rest/seed/nurc:Arc_Sample.json" -Sample response: - -.. code-block:: xml - - * About to connect() to localhost port 8080 (#0) - * Trying 127.0.0.1... connected - * Connected to localhost (127.0.0.1) port 8080 (#0) - * Server auth using Basic with user 'admin' - > POST /geoserver/gwc/rest/seed/nurc:Arc_Sample.json HTTP/1.1 - > Authorization: Basic YWRtaW46Z2Vvc2VydmVy - > User-Agent: curl/7.21.3 (x86_64-pc-linux-gnu) libcurl/7.21.3 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18 - > Host: localhost:8080 - > Accept: */* - > Content-type: application/json - > Content-Length: 205 - > - < HTTP/1.1 200 OK - < Date: Fri, 14 Oct 2011 22:09:21 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Transfer-Encoding: chunked - < - * Connection #0 to host localhost left intact - * Closing connection #0 - - -Querying the running tasks -========================== - -Operations ----------- - -``/gwc/rest/seed[/].json`` +:: + + * About to connect() to localhost port 8080 (#0) + * Trying 127.0.0.1... connected + * Connected to localhost (127.0.0.1) port 8080 (#0) + * Server auth using Basic with user 'admin' + > POST /geoserver/gwc/rest/seed/nurc:Arc_Sample.json HTTP/1.1 + > Authorization: Basic YWRtaW46Z2Vvc2VydmVy + > User-Agent: curl/7.21.3 (x86_64-pc-linux-gnu) libcurl/7.21.3 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18 + > Host: localhost:8080 + > Accept: */* + > Content-type: application/json + > Content-Length: 205 + > + < HTTP/1.1 200 OK + < Date: Fri, 14 Oct 2011 22:09:21 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Transfer-Encoding: chunked + < + * Connection #0 to host localhost left intact + * Closing connection #0 + + +Querying running tasks +---------------------- + +URL: ``/gwc/rest/seed[/].json`` .. list-table:: :header-rows: 1 @@ -186,73 +171,61 @@ Operations - 405 - -Getting the current state of the seeding threads -++++++++++++++++++++++++++++++++++++++++++++++++ - -Sending a GET request to the ``/gwc/rest/seed.json`` resource returns a list of pending (scheduled) and running -tasks for all the layers. +Getting current state of the seeding threads +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Sending a GET reques to the ``/gwc/rest/seed/.json`` resource returns a list of pending (scheduled) and running -tasks for that specific layer. +Sending a GET request to the ``/gwc/rest/seed.json`` resource returns a list of pending (scheduled) and running tasks for all the layers. -The returned content is a JSON array of the form: +Sending a GET request to the ``/gwc/rest/seed/.json`` resource returns a list of pending (scheduled) and running tasks for that specific layer. -.. code-block:: xml +The returned content is a JSON array of the form:: {"long-array-array":[[,,,,],...]} -If there are no pending or running tasks, the returned array is empty: - -.. code-block:: xml +If there are no pending or running tasks, the returned array is empty:: {"long-array-array":[]} -The returned array of arrays contains one array per seeding/truncate Task. -The meaning of each long value in each thread array is: ``[tiles processed, total # of tiles to process, # of remaining tiles, Task ID, Task status]``. -The meaning of the ``Task status`` field is: --1 = ABORTED, -0 = PENDING, -1 = RUNNING, -2 = DONE. +The returned array of arrays contains one array per seeding/truncating task. +The meaning of each long value in each thread array is:: -Sample request: + [tiles processed, total # of tiles to process, # of remaining tiles, Task ID, Task status] -.. code-block:: xml +The meaning of the ``Task status`` field is one of the following:: - curl -u : -v -XGET http://localhost:8080/geoserver/gwc/rest/seed/topp:states.json + -1 = ABORTED + 0 = PENDING + 1 = RUNNING + 2 = DONE -Sample response: +The example below returns the current state of tasks for the ``topp:states`` layer: -.. code-block:: xml +.. code-block:: console - {"long-array-array":[[17888,44739250,18319,1,1],[17744,44739250,18468,2,1],[16608,44739250,19733,3,0],[0,1000,1000,4,1]]} - -In the sample response above tasks ``1`` and ``2`` for the ``topp:states`` layer are running, and -tasks ``3`` and ``4`` are in pending state waiting for an available thread: + curl -u : -v -XGET http://localhost:8080/geoserver/gwc/rest/seed/topp:states.json +.. code-block:: json -Sample request: + {"long-array-array":[[17888,44739250,18319,1,1],[17744,44739250,18468,2,1],[16608,44739250,19733,3,0],[0,1000,1000,4,0]]} + +In the above response, tasks ``1`` and ``2`` for the ``topp:states`` layer are running, and +tasks ``3`` and ``4`` are in a pending state waiting for an available thread. -.. code-block:: xml +The example below returns a list of tasks for all the layers. - curl -u : -XGET http://localhost:8080/geoserver/gwc/rest/seed.json +.. code-block:: console -Sample response: + curl -u : -XGET http://localhost:8080/geoserver/gwc/rest/seed.json -.. code-block:: xml +.. code-block:: json {"long-array-array":[[2240,327426,1564,2,1],[2368,327426,1477,3,1],[2272,327426,1541,4,1],[2176,327426,1611,5,1],[1056,15954794690,79320691,6,1],[1088,15954794690,76987729,7,1],[1040,15954794690,80541010,8,1],[1104,15954794690,75871965,9,1]]} -The sample response response above contains the list of tasks for all the layers. - Terminating running tasks -========================= - -Operations ----------- +------------------------- -``/gwc/rest/seed[/]`` +URL: ``/gwc/rest/seed[/]`` .. list-table:: :header-rows: 1 @@ -278,41 +251,30 @@ Operations - 405 - +A POST request to the ``/gwc/rest/seed`` resource terminates pending and/or running tasks for **all layers**. A POST request to the ``/gwc/rest/seed/`` resource terminates pending and/or running tasks for a specific layer. -A POST request to the ``/gwc/rest/seed`` resource terminates pending and/or running tasks for any layer. - -A POST request to the ``/gwc/rest/seed/`` resource terminates pending and/or running tasks for that specific layer. +It is possible to specify whether to terminate pending and/or running tasks specifically. This is done via the parameter ``kill_all``. This parameter can have one of the following values: ``running``, and ``pending``, and ``all``. -In order to indicate whether to terminate pending and/or running tasks, the form parameter ``"kill_all"`` needs to be specified, -with one of the following values: ``all``, ``running``, ``pending`` (for backwards compatibility, the kill_all parameter -value ``1`` is also accepted and equivalent to ``running``). +.. note:: For backward compatibility, the kill_all parameter value ``1`` is also accepted and is equivalent to ``running``. -For example: ``curl -d "kill_all=all" /rest/seed`` kills both pending and running tasks for any layer, -``curl -d "kill_all=all" /rest/seed/topp:states`` kills only pending tasks for the ``topp:states`` layer, and so on. - The following request terminates all running seed and truncate tasks. -Sample request: +.. code-block:: console -.. code-block:: xml - - curl -v -u admin:geoserver -d "kill_all=all" "http://localhost:8080/geoserver/gwc/rest/seed" + curl -v -u admin:geoserver -d "kill_all=all" "http://localhost:8080/geoserver/gwc/rest/seed" -Sample response: - -.. code-block:: xml - - * About to connect() to localhost port 8080 (#0) - * Trying 127.0.0.1... connected - < HTTP/1.1 200 OK - < Date: Fri, 14 Oct 2011 22:23:04 GMT - < Server: Noelios-Restlet-Engine/1.0..8 - < Content-Type: text/html; charset=ISO-8859-1 - < Content-Length: 426 - < - - ... - * Connection #0 to host localhost left intact - * Closing connection #0 - +:: + + * About to connect() to localhost port 8080 (#0) + * Trying 127.0.0.1... connected + < HTTP/1.1 200 OK + < Date: Fri, 14 Oct 2011 22:23:04 GMT + < Server: Noelios-Restlet-Engine/1.0..8 + < Content-Type: text/html; charset=ISO-8859-1 + < Content-Length: 426 + < + + ... + * Connection #0 to host localhost left intact + * Closing connection #0 From 4ea8d3fdcdbb130892a03f27ab086068b95a3b01 Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Thu, 2 Aug 2012 17:33:02 -0700 Subject: [PATCH 11/72] Edited UI text for Tile Caching section --- .../resources/GeoServerApplication.properties | 208 +++++++++--------- 1 file changed, 102 insertions(+), 106 deletions(-) diff --git a/src/web/gwc/src/main/resources/GeoServerApplication.properties b/src/web/gwc/src/main/resources/GeoServerApplication.properties index 188b506884b..5523fefb87b 100644 --- a/src/web/gwc/src/main/resources/GeoServerApplication.properties +++ b/src/web/gwc/src/main/resources/GeoServerApplication.properties @@ -3,71 +3,71 @@ category.tilecaching = Tile Caching GWCSettingsPage.description = Configure the global settings for the embedded GeoWebCache -GWCSettingsPage.enableWMSIntegration = Enable direct WMS integration -GWCSettingsPage.enableWMSIntegration.title = Allows the regular WMS to serve cached content. Use tiled=true for GetCapabilities in the GeoServer WMS when this option is enabled to include the WMS-C VendorSpecificCapabilities +GWCSettingsPage.enableWMSIntegration = Enable direct integration with GeoServer WMS +GWCSettingsPage.enableWMSIntegration.title = Allows the regular WMS to serve cached content. When this option is enabled, use tiled=true for GetCapabilities in the GeoServer WMS to include the WMS-C VendorSpecificCapabilities GWCSettingsPage.enableWMSC = Enable WMS-C Service -GWCSettingsPage.enableWMSC.title = Enables the integrated GeoWebCache's Caching Web Map Service interface +GWCSettingsPage.enableWMSC.title = Enables the integrated GeoWebCache Caching Web Map Service interface GWCSettingsPage.enableWMTS = Enable WMTS Service -GWCSettingsPage.enableWMTS.title = Enables the integrated GeoWebCache's Web Map Tiling Service interface +GWCSettingsPage.enableWMTS.title = Enables the integrated GeoWebCache Web Map Tiling Service interface GWCSettingsPage.enableTMS = Enable TMS Service -GWCSettingsPage.enableTMS.title = Enables the integrated GeoWebCache's Tiling Map Service interface +GWCSettingsPage.enableTMS.title = Enables the integrated GeoWebCache Tiling Map Service interface -GWCSettingsPage.gwcDemos = Go to the GWC Demos Page -GWCSettingsPage.gwcHome = Go to the GWC Home Page -GWCSettingsPage.title = GeoWebCache Settings +GWCSettingsPage.gwcDemos = Go to the embedded GeoWebCache demo page +GWCSettingsPage.gwcHome = Go to the embedded GeoWebCache home page +GWCSettingsPage.title = Caching Defaults GWCSettingsPage.menuTitle = Caching Defaults -GWCSettingsPage.manageGridSets = Manage GridSets +GWCSettingsPage.manageGridSets = Manage Gridsets GWCSettingsPage.gwcServices= GWC Services GWCSettingsPage.cachingOptions=Default Caching Options -GWCSettingsPage.gwcProvidedServices = GWC Provided Services -GWCSettingsPage.cacheLayersByDefault=Automatically configure a GeoWebCache Layer for every new Layer and LayerGroup +GWCSettingsPage.gwcProvidedServices = Provided Services +GWCSettingsPage.cacheLayersByDefault=Automatically configure a GeoWebCache layer for each new layer or layer group GWCSettingsPage.cacheNonDefaultStyles=Automatically cache non-default styles -GWCSettingsPage.metaTiling=Default meta-tiling factors: +GWCSettingsPage.metaTiling=Default metatile size: GWCSettingsPage.metaTilingX=tiles wide by GWCSettingsPage.metaTilingY=tiles high -GWCSettingsPage.gutter=Default gutter size: +GWCSettingsPage.gutter=Default gutter size in pixels: GWCSettingsPage.defaultCacheOptions=Default Caching Options for GeoServer Layers -GWCSettingsPage.defaultCacheFormats=Default Cache Formats for: +GWCSettingsPage.defaultCacheFormats=Default Tile Image Formats for: GWCSettingsPage.defaultCacheFormatsVector=Vector Layers GWCSettingsPage.defaultCacheFormatsRaster=Raster Layers -GWCSettingsPage.defaultCacheFormatsOther=WMS/Layer Groups +GWCSettingsPage.defaultCacheFormatsOther=Layer Groups GWC.ImageIOFileCachingThresholdUnsetWarning=The "Coverage Access" page "ImageIO Cache Memory Threshold" is unset, \ and can cause a severe performance penalty when seeding tiles. A value of 1024K should suffice for the most common tile sizes. -CachingOptionsPanel.cachedGridsets = Default Cached GridSets -DefaultGridsetsEditor.addDefaultGridSet = Add default GridSet +CachingOptionsPanel.cachedGridsets = Default Cached Gridsets +DefaultGridsetsEditor.addDefaultGridSet = Add default gridset DiskQuotaSettingsPage.title = Disk Quota -DiskQuotaSettingsPage.description = Configure the tile cache disk quota limits and tile expiration policy. -DiskQuotaSettingsPage.disabledWarning = Disk Quota has been disabled through the GWC_DISKQUOTA_DISABLED=true environment variable/servlet context parameter/JVM argument, hence this configuration page is not operational. +DiskQuotaSettingsPage.description = Configure the disk quota limits and expiration policy for the tile cache +DiskQuotaSettingsPage.disabledWarning = Disk Quota has been disabled through the GWC_DISKQUOTA_DISABLED=true environment variable/servlet context parameter/JVM argument, so this configuration page is not operational. DiskQuotaConfigPanel.diskQuota = Disk Quota -DiskQuotaConfigPanel.enableDiskQuota = Enable disk quota limits -DiskQuotaConfigPanel.enableDiskQuota.title = Allow GWC to limit the cache disk size +DiskQuotaConfigPanel.enableDiskQuota = Enable disk quota +DiskQuotaConfigPanel.enableDiskQuota.title = Allow GeoWebCache to limit the cache disk size DiskQuotaConfigPanel.LFU= Least frequently used DiskQuotaConfigPanel.LRU=Least recently used -DiskQuotaConfigPanel.globalQuotaTitle=When forcing disk quota limits, first remove tiles that are: -DiskQuotaConfigPanel.usedQuotaMessage= Used {0} out of {1} available. -DiskQuotaConfigPanel.setGlobalQuota=Set maximum tile cache size: -DiskQuotaConfigPanel.diskBlockSize=Compute cache usage based on a disk block size of: -DiskQuotaConfigPanel.diskBlockSize.title=The file system block size where the cache is stored. Usually 4096, 8194, or 16384. Allows to more accurately compute the disk usage. +DiskQuotaConfigPanel.globalQuotaTitle=When enforcing disk quota limits, remove tiles that are: +DiskQuotaConfigPanel.usedQuotaMessage= Using {0} of a maximum {1} +DiskQuotaConfigPanel.setGlobalQuota=Maximum tile cache size +DiskQuotaConfigPanel.diskBlockSize=Disk block size: +DiskQuotaConfigPanel.diskBlockSize.title=The file system block size where the cache is stored. Usually 4096, 8194, or 16384. Allows a more accurate computation of disk usage. DiskQuotaConfigPanel.bytes=Bytes -DiskQuotaConfigPanel.cleanUpFreq=Check if the cache disk quota is exceeded every: -DiskQuotaConfigPanel.cleanUpFreq.title=Sets the frequency to check whether the cache is oversized to start the clean up process +DiskQuotaConfigPanel.cleanUpFreq=Disk quota check frequency: +DiskQuotaConfigPanel.cleanUpFreq.title=Sets the frequency at which to check whether the cache limit has been exceeded. DiskQuotaConfigPanel.cleanUpFreqUnits=Seconds DiskQuotaConfigPanel.cleanUpLastRun=(Last run: ${x} ${timeUnit} ago.) -DiskQuotaConfigPanel.cleanUpLastRunNever=(Quota never forced since server start up) +DiskQuotaConfigPanel.cleanUpLastRunNever=(Quota limit has not been exceeded since server start up) CachedLayersPage.title = Tile Layers CachedLayersPage.description = Manage the cached layers published by the integrated GeoWebCache -CachedLayersPage.addNew = Add a new cached Layer -CachedLayersPage.removeSelected = Stop caching the selected Layers +CachedLayersPage.addNew = Add a new cached layer +CachedLayersPage.removeSelected = Remove selected cached layers CachedLayersPage.th.type=Type -CachedLayersPage.th.name=Name -CachedLayersPage.th.quotaLimit=Quota Limit +CachedLayersPage.th.name=Layer Name +CachedLayersPage.th.quotaLimit=Disk Quota CachedLayersPage.quotaLimitNotSet=N/A -CachedLayersPage.th.quotaUsed=Quota Used +CachedLayersPage.th.quotaUsed=Disk Used CachedLayersPage.th.enabled=Enabled CachedLayersPage.th.preview=Preview CachedLayersPage.th.actions=Actions @@ -75,101 +75,100 @@ CachedLayersPage.seed=Seed/Truncate CachedLayersPage.truncate = Empty CachedLayersPage.selectOne = Select One CachedLayersPage.previewDisabled = Preview not available -CachedLayersPage.confirmRemoval = Confirm removal of selected Cached Layers -CachedLayersPage.confirmSelectionRemoval = You are about to delete {0} Cached Layers. Their cache will be deleted, amounting to a total of {1} freed from disk. -CachedLayersPage.confirmTruncateTitle = Fully truncate the Layer's tile cache -CachedLayersPage.confirmTruncateMessage = Confirm to delete the whole tile cache for the layer {0}? This operation will free about {1} from disk +CachedLayersPage.confirmRemoval = Confirm removal of cached layers +CachedLayersPage.confirmSelectionRemoval = You are about to remove {0} cached layers. All tiles will be deleted, freeing a total of {1} from disk. +CachedLayersPage.confirmTruncateTitle = Fully truncate the layer's tile cache +CachedLayersPage.confirmTruncateMessage = You are about to remove all cached tiles for layer {0}? This operation will free a total of {1} from disk. geowebcache=GeoWebCache -GridSetsPage.title = Grid Sets -GridSetsPage.description = Manage the available GridSets or create a new one -GridSetsPage.addNew = Create new GridSet -GridSetsPage.addNew.title = Create a new GeoWebCache GridSet from scratch -GridSetsPage.removeSelected = Remove selected GridSets -GridSetsPage.removalLink.title = Remove the selected GridSets and optionally any cached data that references them -GridSetsPage.templateLink = Use as template... -GridSetsPage.templateLink.title = Use this GridSet as a template to create a new one -GridSetsPage.nameLink.title = Click to edit this GridSet -GridSetsPage.nameLink.titleInternalGridSet = This is an internal, non editable GridSet. Click to see it's properties. -GridSetsPage.confirmGridsetsDelete = You are about to delete {0} GridSets, \ - affecting {1} Cached Layers and about {2} of disk space to be freed. All affected layers \ - will be updated and the ones that end up with no associated GridSets will be disabled. - -GridSetListTablePanel.th.name = GridSet -GridSetListTablePanel.th.epsg_code = EPSG code -GridSetListTablePanel.th.tile_dimension = Tile dimensions -GridSetListTablePanel.th.quota_used = Disk usage -GridSetListTablePanel.th.zoom_levels = # of zoom levels - -GridSetEditPage.title = Edit GridSet -GridSetEditPage.description = Change the properties of a GeoWebCache GridSet. Beware that modifying an existing \ -GridSet leads to the elimination of all the cached tiles for all tiled layers that reference it. -GridSetEditPage.internalGridSetMessage = This is an internally defined GridSet and cannot be modified - -GridSetNewPage.title = Create a new GridSet -GridSetNewPage.description = Define a new GridSet for GeoWebCache +GridSetsPage.title = Gridsets +GridSetsPage.description = Manage the available gridsets or create a new one +GridSetsPage.addNew = Create a new gridset +GridSetsPage.addNew.title = Create a new GeoWebCache gridset from scratch +GridSetsPage.removeSelected = Remove selected gridsets +GridSetsPage.removalLink.title = Remove the selected gridsets and optionally any cached data that references them +GridSetsPage.templateLink = Create a copy +GridSetsPage.templateLink.title = Use this gridset as a template for creating a new one +GridSetsPage.nameLink.title = Click to edit this gridset +GridSetsPage.nameLink.titleInternalGridSet = This is an internally defined gridset and cannot be modified. +GridSetsPage.confirmGridsetsDelete = You are about to delete {0} gridsets, \ + affecting {1} cached layers. A total of {2} of disk space will be freed. This may cause cached layers with no other associated gridsets to be disabled. + +GridSetListTablePanel.th.name = Gridset +GridSetListTablePanel.th.epsg_code = CRS +GridSetListTablePanel.th.tile_dimension = Tile Dimensions +GridSetListTablePanel.th.quota_used = Disk Usage +GridSetListTablePanel.th.zoom_levels = Zoom levels + +GridSetEditPage.title = Edit gridset +GridSetEditPage.description = Change the properties of a GeoWebCache gridset. Modifying an existing \ +gridset leads to the removal of all cached tiles for every layers that reference it. +GridSetEditPage.internalGridSetMessage = This is an internally defined gridset and cannot be modified + +GridSetNewPage.title = Create a new gridset +GridSetNewPage.description = Define a new gridset for GeoWebCache AbstractGridSetPage.name = Name AbstractGridSetPage.descriptionField = Description -AbstractGridSetPage.gridSetAlreadyExists = A GridSet named '${name}' already exists +AbstractGridSetPage.gridSetAlreadyExists = A gridset named '${name}' already exists AbstractGridSetPage.crs = Coordinate Reference System -AbstractGridSetPage.tileWidth = Tile width in pixels (16 - 2048) -AbstractGridSetPage.tileHeight = Tile height in pixels (16 - 2048) -AbstractGridSetPage.bounds = GridSet bounds -AbstractGridSetPage.computeBounds = Compute from CRS Area of Validity +AbstractGridSetPage.tileWidth = Tile width in pixels +AbstractGridSetPage.tileHeight = Tile height in pixels +AbstractGridSetPage.bounds = Gridset bounds +AbstractGridSetPage.computeBounds = Compute from maximum extent of CRS AbstractGridsetPage.computeBounds.crsNotSet = Can't compute bounds, CRS is not set -AbstractGridsetPage.computeBounds.aovNotSet = Can't compmute bounds, CRS doesn't provide an area of validity +AbstractGridsetPage.computeBounds.aovNotSet = Can't compute bounds, CRS doesn't provide an area of validity AbstractGridSetPage.tileMatrixSet = Tile Matrix Set AbstractGridSetPage$GridSetCRSPanel.units = Units: AbstractGridSetPage$GridSetCRSPanel.metersPerUnit = Meters per unit: AbstractGridSetPage.addZoomLevel = Add zoom level -AbstractGridSetPage.cantAddZoomLevel = The GridSet CRS and bounds need to be set before adding zoom levels +AbstractGridSetPage.cantAddZoomLevel = Gridset CRS and bounds must be set before adding zoom levels -TileMatrixSetEditor.defineGridsOn = Define grids based on +TileMatrixSetEditor.defineGridsOn = Define grids based on: TileMatrixSetEditor.level = Level TileMatrixSetEditor.level.title = Zoom level -TileMatrixSetEditor.resolution = Pixel size +TileMatrixSetEditor.resolution = Pixel Size TileMatrixSetEditor.resolution.title = Pixel size in map units (resolution) TileMatrixSetEditor.scale = Scale -TileMatrixSetEditor.scale.title = Scale denominator based on the prescribed display pixel size of 0.0028m +TileMatrixSetEditor.scale.title = Scale denominator based on a display pixel size of 0.0028m TileMatrixSetEditor.name = Name TileMatrixSetEditor.name.title = An optional name for each tile matrix -TileMatrixSetEditor.tiles = Tiles -TileMatrixSetEditor.tiles.title = Number for tiles wide and high each tile matrix spans for the whole GridSet bounds +TileMatrixSetEditor.tiles = Tiles +TileMatrixSetEditor.tiles.title = Number of tiles (width x height) each tile matrix uses to span the gridset extent TileMatrixSetEditor.removeLink = Remove this zoom level -TileMatrixSetEditor.validation.empty = GridSet has no grids defined +TileMatrixSetEditor.validation.empty = Gridset has no grids defined LayerCacheOptionsTabPanel.title = Tile Caching -LayerCacheOptionsTabPanel.shortDescription = Configure the Layer's tile caching options +LayerCacheOptionsTabPanel.shortDescription = Configure tile caching options for the layer LayerGroupCacheOptionsPanel.title = Tile Caching -LayerGroupCacheOptionsPanel.shortDescription = Configure the LayerGroup's tile caching options +LayerGroupCacheOptionsPanel.shortDescription = Configure tile caching options for the layer group -GeoServerTileLayerEditor.title = Tile caching configuration -GeoServerTileLayerEditor.createTileLayerForLayer = Create a Cached Layer for this Layer -GeoServerTileLayerEditor.createTileLayerForLayerGroup = Create a Cached Layer for this Layer Group +GeoServerTileLayerEditor.title = Tile cache configuration +GeoServerTileLayerEditor.createTileLayerForLayer = Create a cached layer for this layer +GeoServerTileLayerEditor.createTileLayerForLayerGroup = Create a cached layer for this layer group GeoServerTileLayerEditor.createTileLayer.title = Check this option to create a GeoWebCache cached layer for this resource GeoServerTileLayerEditor.enabled = Enable tile caching for this layer GeoServerTileLayerEditor.enabled.title = If not checked the GeoWebCache layer for this resource will not be available for service requests \ but it will still be possible to seed or truncate its cache. -GeoServerTileLayerEditor.metaTiling = Meta tiling factors +GeoServerTileLayerEditor.metaTiling = Metatiling factors GeoServerTileLayerEditor.metaTilingX = tiles wide by GeoServerTileLayerEditor.metaTilingY = tiles high -GeoServerTileLayerEditor.gutter = Gutter size (pixels) -GeoServerTileLayerEditor.cacheFormats = Cache image formats -GeoServerTileLayerEditor.cachedGridsets = Cached Grid Sets +GeoServerTileLayerEditor.gutter = Gutter size in pixels +GeoServerTileLayerEditor.cacheFormats = Tile Image Formats +GeoServerTileLayerEditor.cachedGridsets = Available gridsets GeoServerTileLayerEditor.parameterFilters = Parameter Filters GeoServerTileLayerEditor.cacheNonDefaultStyles = Create a separate cache for each STYLE GeoServerTileLayerEditor.timeParameterFilter = Create a separate cache for the TIME WMS parameter GeoServerTileLayerEditor.elevationParameterFilter = Create a separate cache for the ELEVATION WMS parameter GeoServerTileLayerEditor.confirmTileLayerRemoval = Confirm removal of existing cached layer for this resource? \ - This will remove the Layer tile cache freeing up about {0} of cache space. + This will remove the layer tile cache, freeing up {0}. GeoServerTileLayerEditor.timeParameterFilterDisabled = To enable caching by the TIME parameter, assign a time attribute in the Dimensions tab GeoServerTileLayerEditor.elevationParameterFilterDisabled = To enable caching by the ELEVATION parameter, assign an elevation attribute in the Dimensions tab - -GridSubsetsEditor.gridSet = GridSet + +GridSubsetsEditor.gridSet = Gridset GridSubsetsEditor.zoomStart.nullValid = Min GridSubsetsEditor.zoomStop.nullValid = Max GridSubsetsEditor.minCachedLevel.nullValid = Min @@ -178,26 +177,23 @@ GridSubsetsEditor.publishedLevels = Published zoom levels GridSubsetsEditor.cachedLevels = Cached zoom levels GridSubsetsEditor.minCachedLevel = Min cached level GridSubsetsEditor.maxCachedLevel = Max cached level -GridSubsetsEditor.bounds = Subset bounds +GridSubsetsEditor.bounds = Grid subset bounds GridSubsetsEditor.removeLink = Remove -GridSubsetsEditor.addGridSubsetFor = Add Grid subset for GridSet: -GridSubsetsEditor.validation.empty = No GridSubsets defined +GridSubsetsEditor.addGridSubsetFor = Add grid subset: +GridSubsetsEditor.validation.empty = No grid subsets defined GridSubsetsEditor.validation.zoomLevelsError = Zoom start can't be higher than zoom stop -GridSubsetsEditor.validation.gridSetNotFound = GridSet {0} does not exist -GridSubsetsEditor.validation.invalidBounds = GridSubset bounds are either null or invalid -GridSubsetsEditor.validation.boundsOutsideCoverage = Grid Subset bounds do not intersect the GridSet bounds +GridSubsetsEditor.validation.gridSetNotFound = Gridset {0} does not exist +GridSubsetsEditor.validation.invalidBounds = Grid subset bounds are either null or invalid +GridSubsetsEditor.validation.boundsOutsideCoverage = Grid subset bounds do not intersect the Gridset bounds GridSubsetsEditor.bounds.dynamic = Dynamic -CachingOptionsPanel.validation.emptyGridsets = If automatic configuration of new layers is enabled at least one default GridSet shall be defined -CachingOptionsPanel.validation.emptyCacheFormatList = If automatic configuration of new layers is enabled at least one cache image format shall be set for each group +CachingOptionsPanel.validation.emptyGridsets = If automatic configuration of new layers is enabled at least one default gridset shall be defined +CachingOptionsPanel.validation.emptyCacheFormatList = If automatic configuration of new layers is enabled at least one tile image format shall be set for each group NewCachedLayerPage.title = New Cached Layer -NewCachedLayerPage.description = Click on a GeoServer Layer or LayerGroup name to set up a Cached Layer for it, \ -or use the bulk configuration link bellow to create a Cached Layer for all the selected elements in the list \ -using the default settings from the GeoWebCache configuration page. +NewCachedLayerPage.description = Click on the name of a layer in the list below to configure a cached layer, or select one or more layers and use the link below to configure all layers with default options. -NewCachedLayerPage.bulkConfig = Configure a cached layer for all selected layers using the default settings -NewCachedLayerPage.bulkConfig.insaneDefaults = Cannot enable bulk config, check the default settings in the GeoWebCache config page -NewCachedLayerPage.confirmBulkConfig.title = Bulk config of Cached Layers -NewCachedLayerPage.confirmBulkConfig.message = Confirm the creation of a Cached Layer for each of the {0} selected \ -GeoServer layer or layer groups?

The globally defined defaults from the GeoWebCache configuration page will be used.

+NewCachedLayerPage.bulkConfig = Configure selected layers with caching defaults +NewCachedLayerPage.bulkConfig.insaneDefaults = Cannot enable automatic configuration, check the default caching settings +NewCachedLayerPage.confirmBulkConfig.title = Automatic configuration of cached layers +NewCachedLayerPage.confirmBulkConfig.message = You are about to create a cached layer for each of the {0} selected layers.

The default caching settings will be used.

From 00d1706835b5159d50f86082ef1bf1b50e0e40ee Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Fri, 3 Aug 2012 15:42:13 -0700 Subject: [PATCH 12/72] Added docs on the new Tile Caching section in the UI --- doc/en/user/source/geowebcache/using.rst | 4 +- doc/en/user/source/webadmin/index.rst | 5 +- .../source/webadmin/tilecache/defaults.rst | 162 +++++++++++++----- .../source/webadmin/tilecache/diskquotas.rst | 45 +++++ .../source/webadmin/tilecache/gridsets.rst | 98 +++++++++++ .../tilecache/img/addexistinggridset.png | Bin 0 -> 5175 bytes .../tilecache/img/defaults_formats.png | Bin 0 -> 3131 bytes .../tilecache/img/defaults_gridsets.png | Bin 0 -> 5172 bytes .../tilecache/img/defaults_options.png | Bin 0 -> 4990 bytes .../tilecache/img/defaults_services.png | Bin 0 -> 5274 bytes .../webadmin/tilecache/img/diskquota.png | Bin 0 -> 9534 bytes .../webadmin/tilecache/img/editgridset.png | Bin 0 -> 20904 bytes .../webadmin/tilecache/img/gridsets.png | Bin 0 -> 16893 bytes .../tilecache/img/gridsets_matrix.png | Bin 0 -> 9327 bytes .../webadmin/tilecache/img/gridsets_new.png | Bin 0 -> 10286 bytes .../source/webadmin/tilecache/img/menu.png | Bin 0 -> 3017 bytes .../webadmin/tilecache/img/newcachedlayer.png | Bin 0 -> 8832 bytes .../tilecache/img/readonlygridset.png | Bin 0 -> 4200 bytes .../tilecache/img/removecachedlayers.png | Bin 0 -> 4541 bytes .../webadmin/tilecache/img/removegridset.png | Bin 0 -> 7573 bytes .../webadmin/tilecache/img/tilelayers.png | Bin 0 -> 17804 bytes .../user/source/webadmin/tilecache/index.rst | 17 +- .../user/source/webadmin/tilecache/layers.rst | 69 ++++++++ 23 files changed, 351 insertions(+), 49 deletions(-) create mode 100644 doc/en/user/source/webadmin/tilecache/diskquotas.rst create mode 100644 doc/en/user/source/webadmin/tilecache/img/addexistinggridset.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/defaults_formats.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/defaults_gridsets.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/defaults_options.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/defaults_services.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/diskquota.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/editgridset.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/gridsets.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/gridsets_matrix.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/gridsets_new.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/menu.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/newcachedlayer.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/readonlygridset.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/removecachedlayers.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/removegridset.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/tilelayers.png diff --git a/doc/en/user/source/geowebcache/using.rst b/doc/en/user/source/geowebcache/using.rst index a9cb270c54b..30f4ba98b88 100644 --- a/doc/en/user/source/geowebcache/using.rst +++ b/doc/en/user/source/geowebcache/using.rst @@ -10,7 +10,7 @@ GeoWebCache integration with GeoServer WMS GeoWebCache (as of GeoServer 2.1.0) is transparently integrated with the GeoServer WMS, and so requires no special endpoint or custom URL in order to be used. In this way one can have the simplicity of a standard WMS endpoint with the performance of a tiled client. -This direct integration is turned off by default. It can be enabled by going to the :ref:`webadmin_gwc` page in the :ref:`web_admin`. +This direct integration is turned off by default. It can be enabled by going to the :ref:`webadmin_tilecaching` page in the :ref:`web_admin`. When this feature is enabled, GeoServer WMS will cache and retrieve tiles from GeoWebCache (via a GetMap request) **only if the following conditions apply**: @@ -50,7 +50,7 @@ As soon as tiles are requested through GeoWebCache, GeoWebCache automatically st Disk quota ---------- -GeoWebCache has a built-in disk quota feature to prevent disk space from growing unbounded. Disk quotas are turned off by default, but can be configured on the :ref:`webadmin_gwc` page in the :ref:`web_admin`. You can set the maximum size of the cache directory, poll interval, and what policy of tile removal to use when the quota is exceeded. Tiles can be removed based on usage ("Least Frequently Used" or LFU) or timestamp ("Least Recently Used" or LRU). +GeoWebCache has a built-in disk quota feature to prevent disk space from growing unbounded. Disk quotas are turned off by default, but can be configured on the :ref:`webadmin_tilecaching` page in the :ref:`web_admin`. You can set the maximum size of the cache directory, poll interval, and what policy of tile removal to use when the quota is exceeded. Tiles can be removed based on usage ("Least Frequently Used" or LFU) or timestamp ("Least Recently Used" or LRU). Integration with external mapping sites --------------------------------------- diff --git a/doc/en/user/source/webadmin/index.rst b/doc/en/user/source/webadmin/index.rst index 4d0df0ab57f..345db391ab3 100644 --- a/doc/en/user/source/webadmin/index.rst +++ b/doc/en/user/source/webadmin/index.rst @@ -8,10 +8,11 @@ The Web Administration Interface is a web-based tool for configuring all aspects .. toctree:: :maxdepth: 2 - basics.rst - layerpreview/index + basics server/index + layerpreview/index data/index services/index + tilecache/index security/index demos/index diff --git a/doc/en/user/source/webadmin/tilecache/defaults.rst b/doc/en/user/source/webadmin/tilecache/defaults.rst index 484d6087ecf..2004cbcf68c 100644 --- a/doc/en/user/source/webadmin/tilecache/defaults.rst +++ b/doc/en/user/source/webadmin/tilecache/defaults.rst @@ -1,59 +1,141 @@ -.. _webadmin_gwc: +.. _webadmin_tilecaching_defaults: -GeoWebCache Settings -==================== +Caching defaults +================ -The GeoWebCache Settings page in the Server menu in the :ref:`web_admin` shows some configuration options for GeoWebCache, a tile server that comes embedded by default inside GeoServer. For more information about this embedded version, please see the section on :ref:`geowebcache`. +The Caching Defaults page shows the global configuration options for the tile caching functionality in GeoServer, an embedded GeoWebCache. -.. figure:: img/gwcsettings.png +.. note:: For more information about this embedded version, please see the section on :ref:`geowebcache`. + +GWC Provided Services +--------------------- + +GeoWebCache provides additional endpoints for OGC services to the standard GeoServer endpoints. For example, the GeoServer WMS endpoint is available at:: + + http://GEOSERVER_URL/wms?... + +whereas the GeoWebCache WMS endpoint is:: + + http://GEOSERVER_URL/gwc/service/wms?... + +.. figure:: img/defaults_services.png :align: center -Enable direct WMS integration ------------------------------ + *Provided services* + +The following settings describe the different services that can be enabled with GeoWebCache. + +Enable direct integration with GeoServer WMS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Direct integration allows WMS requests served through GeoServer to be cached as if they were received and processed by GeoWebCache. This gives all the advantages of using a tile server while still employing the more-flexible GeoServer WMS as a fallback. See the section on :ref:`gwc_using` for more details about this feature. + +When this setting is enabled, tile caching will be enabled for all standard WMS requests that contain the ``tiled=true`` parameter and conform to all required parameters. + +This setting is disabled by default. When enabling this option, it is a good idea to also turn on :ref:`webadmin_tilecaching_diskquotas` as well, to prevent unbounded growth of the stored tiles. + +Enable WMS-C Service +~~~~~~~~~~~~~~~~~~~~ -GeoWebCache acts as a proxy between GeoServer and map client. By default, GeoWebCache has a separate endpoint from the GeoServer WMS. (See the section on :ref:`gwc_using` for more details.) +Enables the Cached Web Map Service (WMS-C) service. When this setting is enabled, GeoWebCache will respond to its own WMS-C endpoint:: -Enabling direct WMS integration allows WMS requests served through GeoServer to be cached as if they were received and processed by GeoWebCache. This yields the flexibility of a WMS with the speed of a tile server. See the section on :ref:`gwc_using` for more details about this feature. + http://GEOSERVER_URL/gwc/service/wms?SERVICE=WMS&VERSION=1.1.1&TILED=true&... -Disk quota ----------- +When the service is disabled, calls to the capabilities document will return a ``Service is disabled`` message. -This section manages the disk usage for tiles saved with GeoWebCache. +Enable TMS Service +~~~~~~~~~~~~~~~~~~ + +Enables the Tiled Map Service (TMS) endpoint in GeoWebCache. When this setting is enabled, GeoWebCache will respond to its own TMS endpoint:: + + http://GEOSERVER/URL/gwc/service/tms/1.0.0 + +When the service is disabled, calls to the capabilities document will return a ``Service is disabled`` message. + +Enable WMTS Service +~~~~~~~~~~~~~~~~~~~ + +Enables the Web Map Tiled Service (WMTS) endpoint in GeoWebCache. When this setting is enabled, GeoWebCache will respond to its own WMTS endpoint:: + + http://GEOSERVER/URL/gwc/service/wmts?... + +When the service is disabled, calls to the capabilities document will return a ``Service is disabled`` message. + + +Default Caching Options for GeoServer Layers +-------------------------------------------- + +This section allows for the configuration of the various defaults and other global options for the tile cache in GeoServer. + +.. figure:: img/defaults_options.png + :align: center -By default, disk usage with GeoWebCache is unbounded, regardless of integration with the GeoServer WMS, so every tile served from GeoWebCache will be stored in the cache directory (typically the :file:`gwc` directory inside the data directory). When direct WMS integration is enabled but disk quotas not enabled, every tile that is served through both the GeoServer WMS and GeoWebCache will be stored in the cache directory, which could cause disk capacity issues. Setting a disk quota allows disk usage to be constrained. + *Default caching options* -.. list-table:: - :widths: 30 15 55 - :header-rows: 1 +Automatically configure a GeoWebCache layer for each new layer or layer group +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - Option - - Default value - - Description - * - :guilabel:`Enable Disk Quota limits` - - Off - - Turns on the disk quota. When disabled, the cache directory will grow unbounded. When enabled, the disk quota will be set according to the options below. - * - :guilabel:`Compute cache usage based on a disk block size of` - - 4096 bytes - - This field should be set equal to the disk block size of the storage medium where the cache is located. - * - :guilabel:`Check if the cache disk quota is exceeded every` - - 10 seconds - - Time interval at which the cache is polled. Smaller values (more frequent polling) will slightly increase disk activity, but larger values (less frequent polling) might cause the disk quota to be temporarily exceeded. - * - :guilabel:`Set maximum tile cache size` - - 100 MiB (Mebibytes) - - The maximum size for the cache. When this value is exceeded and the cache is polled, tiles will be removed according to the policy choice listed below. Note that the unit options are **mebibytes** (approx. 1.05MB), **gibibytes** (approx. 1.07GB), and **tebibytes** (approx. 1.10TB). - * - :guilabel:`When forcing disk quota limits, remove first tiles that are` - - Least Frequently Used - - Sets the policy for tile removal when the disk quota is exceeded. Options are **Least Frequently Used** (removes tiles based on how often the tile was accessed) or **Least Recently Used** (removes tiles based on date of last access). +This setting affects how layers in GeoServer are handled via the embedded GeoWebCache. When this setting is enabled, an entry in the GeoWebCache layer listing will be created whenever a new layer or layer group is published in GeoServer. Use this setting to keep the GeoWebCache catalog in sync. (This is enabled by default.) -.. note:: See the `GeoWebCache documentation `_ for more about disk quotas. +Automatically cache non-default styles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When finished making changes, click :guilabel:`Submit`. +By default, only requests using the default style for a given layer will be cached. When this setting is enabled, all requests for a given layer, even those that use a non-standard style will be cached. Disabling this may be useful in situations where disk space is an issue, or when only one default style is important. -This section also shows how much disk space is being used compared to the disk quota size, as well as the last time (if any) the quota was reached. +Default metatile size +~~~~~~~~~~~~~~~~~~~~~ +A metatile is several tiles combined into a larger one. This larger metatile is generated and then sliced up before being served back (and cached) as standard tiles. The advantage of using metatiling is in situations where a label or geometry lies on a boundary of a tile, which might get cut off or altered. With metatiling, these tile edge issues are greatly reduced. -Links ------ +The disadvantage of metatiling is that each WMS rendered tile is much larger, causing a possible performance penalty. Also, at much large sizes, memory consumption can be an issue. + +The size of the default metatile can be adjusted here. By default, GeoServer sets a metatile size of **4x4**, which strikes a balance between performance, memory usage, and rendering accuracy. + +Default gutter size +~~~~~~~~~~~~~~~~~~~ + +The gutter size sets the amount of extra space (in pixels) used when generating a tile. Use this in conjunction with metatiles in order to reduce problems with labels and features not being rendered incorrectly due to being on a tile boundary. + +Default Cache Formats +~~~~~~~~~~~~~~~~~~~~~ + +This setting determines the default image formats that can be cached when tiled requests are made. There are four image formats that can be used when saving tiles: + +* PNG (24-bit PNG) +* PNG8 (8-bit PNG) +* JPEG +* GIF + +The default settings are subdivided into vector layers, raster layers, and layer groups. You may select any of the above four formats for each of the three types of layers. Any requests that fall outside of these layer/format combinations will not be cached if sent through GeoServer, and will return an error if sent to the GeoWebCache endpoints. + +These defaults can be overwritten on a per-layer basis when :ref:`editing the layer properties `. + +.. figure:: img/defaults_formats.png + :align: center + + *Default image formats* + + + +Default Cached Gridsets +~~~~~~~~~~~~~~~~~~~~~~~ + +This section shows the gridsets that will be automatically configured for cached layers. While there are a few pre-configured gridsets available by default, there are only two gridsets enabled by default. These correspond to the most common and universal cases: + +* EPSG:4326 (geographic) with 22 maximum zoom levels and 256x256 pixel tiles +* EPSG:900913 (spherical Mercator) with 31 maximum zoom levels and 256x256 pixel tiles. + +.. figure:: img/defaults_gridsets.png + :align: center + + *Default gridsets* + + +To add a pre-existing grid set, select it from the :guilabel:`Add default grid set` drop down menu, and click the Add icon (green circle with plus sign). + +.. figure:: img/addexistinggridset.png + :align: center -This page contains links to the embedded GWC homepage (containing runtime statistics and status updates) and :ref:`gwc_demo` where you can view all layers known to GeoWebCache and reload configuration. + *Adding an existing gridset to the list of defaults* +These definitions are explored in more detail on the :ref:`webadmin_tilecaching_gridsets` page. diff --git a/doc/en/user/source/webadmin/tilecache/diskquotas.rst b/doc/en/user/source/webadmin/tilecache/diskquotas.rst new file mode 100644 index 00000000000..bba90a2ad3e --- /dev/null +++ b/doc/en/user/source/webadmin/tilecache/diskquotas.rst @@ -0,0 +1,45 @@ +.. _webadmin_tilecaching_diskquotas: + +Disk Quotas +=========== + +This section manages the disk usage for cached tiles. + +By default, disk usage for cached tiles is unbounded, but this can cause disk capacity issues, especially when using Direct WMS integration (see :ref:`webadmin_tilecaching_diskquotas` for more on this). Setting a disk quota allows disk usage to be constrained. + +This page sets the global disk quota; individual layer quotas can be set in the layer's :ref:`properties ` page. + +When finished making any changes, remember to click :guilabel:`Submit`. + +.. figure:: img/diskquota.png + :align: center + + *Disk quota* + +Enable disk quota +----------------- + +When enabled, the disk quota will be set according to the options listed below. The setting is disabled by default. + +Disk block size +--------------- + +This setting determines how the tile cache calculates disk usage. This field should be set equal to the disk block size of the storage medium where the cache is located. The default is **4096 bytes**. + +Disk quota check frequency +-------------------------- + +The time interval where the cache is polled for any overage. Smaller values (more frequent polling) will slightly increase disk activity, but larger values (less frequent polling) may cause the disk quota to be temporarily exceeded. The default is **10 seconds**. + +Maximum tile cache size +----------------------- + +The maximum size for the cache. When this value is exceeded and the cache is polled, tiles will be removed according to the policy. Note that the unit options are **mebibytes (MiB)** (approx. 1.05MB), **gibibytes (GiB)** (approx. 1.07GB), and **tebibytes (TiB)** (approx. 1.10TB). Default is **500 MiB**. + +Below this setting, a graphic showing the current utilized size of the cache relative to the disk quota is shown. + +Tile removal policy +------------------- + +When the disk quota is exceeded, this policy sets how the tiles to be deleted are determined. Options are **Least Frequently Used** (removes tiles based on how often the tile was accessed) or **Least Recently Used** (removes tiles based on date of last access). The best option is dependent on your data and server usage. + diff --git a/doc/en/user/source/webadmin/tilecache/gridsets.rst b/doc/en/user/source/webadmin/tilecache/gridsets.rst index e69de29bb2d..b3203c628d4 100644 --- a/doc/en/user/source/webadmin/tilecache/gridsets.rst +++ b/doc/en/user/source/webadmin/tilecache/gridsets.rst @@ -0,0 +1,98 @@ +.. _webadmin_tilecaching_gridsets: + +Gridsets +======== + +A gridset is a definition that specifies a spatial reference system, bounding box (extent), a list of zoom levels (resolutions or scale denominators), and tile dimensions. Tile requests must conform to the gridset matrix, otherwise caching will not occur. + +This page allows you to edit existing saved gridsets or create new ones. There are only two coordinate reference systems (CRS) available in the default gridsets (EPSG:4326, EPSG:900913), so a new gridset will need to be created to support an additional CRS. Another reason to create a new gridset would be to set a different tile size, or different number of zoom levels. + +.. figure:: img/gridsets.png + :align: center + + *Gridsets menu* + +Creating a new gridset +---------------------- + +To create a new gridset, click on the :guilabel:`Create new gridset`. You will then be asked to enter a range of parameters. + +.. figure:: img/gridsets_new.png + :align: center + + *Creating a new gridset* + +Name +~~~~ + +The short name of the new gridset. + +Description +~~~~~~~~~~~ + +Metadata on the gridset. + +Coordinate Reference System +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The CRS to use in the gridset. You can select from any CRS that GeoServer recognizes. After selection, both the units (meters, feet, degrees, etc.) and the number of meters per unit will be displayed. + +Gridset bounds +~~~~~~~~~~~~~~ + +Sets the maximum extent for the gridset. Typically this is set to be the maximum extent of the CRS used, but a smaller value can be substituted if desired. To populate the max extent in the fields, click :guilabel:`Compute from maximum extent of CRS`. + +Tile width and height +~~~~~~~~~~~~~~~~~~~~~ + +Sets the tile dimensions. Default is **256x256 pixels**. The tile dimensions can be anything from 16 to 2048 pixels. In addition, the tiles need not be square. + +Tile matrix set +~~~~~~~~~~~~~~~ + +The tile matrix set (or tile pyramid) is a list of zoom levels containing ever increasing amounts of tiles. This three dimensional collection of tile "slots" creates the framework where actual image tiles will be saved. You can define the tile matrix based on resolutions or scale denominators. + +Click :guilabel:`Add zoom level` to generate the first zoom level. The parameters will be automatically configured such that the full extent of will be contained by a single pixel's height. The number of pixels in a given zoom level will be displayed, along with the Pixel Size, Scale, and an optional Name, where you can give a name to each zoom level if desired. + +Typically each additional zoom level is twice as large in each dimension, and so contains four times as many tiles as the previous zoom level. The actual values will be populated automatically during subsequent clicking of the :guilabel:`Add zoom level` link. These defaults are usually sufficient, and you need only determine the maximum number of zoom levels desired for this gridset. + +When finished, click :guilabel:`Save`. Before you will be able to use this new gridset with a layer, you will need to add this gridset to the layer's list of available gridsets. This is done on an individual layer's :ref:`properties ` page. You can also add this gridset to the default list on the :ref:`webadmin_tilecaching_defaults` page. + +.. figure:: img/gridsets_matrix.png + :align: center + + *Tile matrix set* + + +Editing a gridset +----------------- + +Clicking on an existing gridset will open it for editing. Please note that the built-in gridsets cannot be edited. They can, however, be copied. + +.. figure:: img/editgridset.png + :align: center + + *Editing a gridset* + + +.. figure:: img/readonlygridset.png + :align: center + + *This gridset is read-only* + +Copying a gridset +----------------- + +As there are many configuration options to a gridset, it is often more convenient to copy an existing gridset. For any of the existing gridsets, click the :guilabel:`Create a copy` link to copy the gridset information to a new gridset. + +Removing a gridset +------------------ + +To remove a gridset, check the box next to the entry or entries, and click :guilabel:`Remove selected gridsets`. + +.. warning:: Removing a gridset definition will remove not only the gridset definition, but also any tiles on any layers generated with this gridset. + +.. figure:: img/removegridset.png + :align: center + + *Removing a gridset* diff --git a/doc/en/user/source/webadmin/tilecache/img/addexistinggridset.png b/doc/en/user/source/webadmin/tilecache/img/addexistinggridset.png new file mode 100644 index 0000000000000000000000000000000000000000..4640b9e37db4a7db7d428a34ad1cd8973462c8e3 GIT binary patch literal 5175 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 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/defaults_formats.png b/doc/en/user/source/webadmin/tilecache/img/defaults_formats.png new file mode 100644 index 0000000000000000000000000000000000000000..67fc8ebb13dec6befcac3c7fde09007b7669a013 GIT binary patch literal 3131 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 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/defaults_gridsets.png b/doc/en/user/source/webadmin/tilecache/img/defaults_gridsets.png new file mode 100644 index 0000000000000000000000000000000000000000..7635af77d9bf3e8893df9264766b50f960042dde GIT binary patch literal 5172 zcmaJ_XH=6*w|;>fAU1^96{$uLMGz1v3aEfULW^_}q>6-I1t|uVCJ>5}5UMn3qCg_O zNrzA)HB^OALPtyxcrWMP`{%poyWgLgS?gJAX3w+tv-h3}*VR@(dg$CC0053^Jh-n9 z01)WG8pp|YFn%diRy&x^da4?GqFn4eeV(}60xC8xR<`^aPEYJ@^=+TnJa=oiRRjPw zKo{{)74Xiubjgk6B;-T{U*R)^gapZjb&H!LAQ3%^RU?6*x>t^-m)(<0J(g(l=ZaoB zoep5ihZZW-)v#BBC?H6578V)>!L6+=>?o)nJKECTSyfdPt1bkNV#z;qDM44j`CcJ) z?7+Z)kh($TR-cowItY%o)THhAHFpkgstY+8J^W(?*`Z@_D0-`>f5x)BkO`_2~IeF<1gw?U?YBxWPju6XNwx>Z56alnT zKX+b$S6_$% z6VtQW*7yZ89nG{ZeL$DKy#98saIvlJQ)xxpQ>E;WRO#p_7Gy5JsCue~Mhg@qZ|;NEp&8jzgjZ9^ z6AKI?8jVE@y48VB>e%-mYEghF5SsRVp*M8^)WD~7^$sskhAJ1qjm_=M#?-;#(Sqzc z5de6gu13VF)qnU(d8`*vF(v|Bei~oVIoCY3(jMA04*()05*n?h+A>9Oip7o+(VM%I z1GFyyJ_Qhkhb2`FRksNgqywTlM6}ugJQ|CRjU5%hr|63Ua}x{EF);`622z6ZL2!3> zH*u8=g82t=G1F?d!C2VdSp)M8g66@Q)$o??R4FOxd;_3uIt?($pGwh48V3RIbZ{J> zpa)PWK?eA7w^s7ZYQCBpdItordDjBEp&-ZvvZiSB^As?@(Llk#0B;c0d&RdHi^i^Q z(=+m&E`@%SHmf$7~;yp$|0S&rrrNJAvGLjVA)auNV6 zSpfh60Ko^6mS6z@H~`ciK)3^rVgCZ2mr=QAKAt-Ka;H+rF`#c_V5HKiAj^F32*7Uw zqYRzhUM=DCp}*b!_Nms|p6q?wk%!MD4SH-P+hrohZ=^I1+PL}`Anc40YN#cBR$p;c z;3<5D;Z+#(DVBvx^bc^3nr0yv;MQ30KXdy7qm^(1F6>XE8@qN z$9&Y`pRIkQb2^!ORmb-C#8&#ow%lu4G^&c1mCxgqd&2moWrAGTqyc3=M6dF_o6rVa z@b=J+m&u7bPq=8ErHYFMP!aFp7O9w>H3S<6kYtJs#sa~Vu49dDL7@03`I8MRGLx2w z<9`I}QY&Q}WYOWhtKM5Z6_)dUwa9{W2nsxCgiy?uo?i;~DfRM4Z}G{#{B%a1=HQ)P zJ0*NI{AqKqq>87V#TYlzsm_Oa;}T$5fK1onw&lgje_Ci-E+(!HwgzWKypbRCy7ZB@ z>D{br$o|uv)+ew^S|!1IrH*b4c=pBC{{A+MLIBEw|JRET|KZv9geiF-z2pCNdwsD) z1K#7esh+J_b4J(EggZo{yNHyW-Zl6~vJKjyDtjl9kbYAtsl&>PP(7&-lq31gtfOKj zi?`h!zNs;%R!!~bPI>@=&Za%cQ_O|Hww+rn=3v=`uf2$fU;GORK8cwi4?LY?}`zUs*h;PBPB zTvVKuxDxYJjL8e1dChap#z8CLTewjw7dd0R@^$~k9^1?t9`^BZ{p&^7BhCJhqAy>5 z*36y(Wk7z%yU8c@DPY$dCkv9&4SDS&@Rwm-v&b?hD~z+piZ&DjWoEg$ z&scNOdKyf|`^5h~C*H*&qy!$eVY_keSE8QMVBk4giRy;s`K)gd}{L3gasC^TT#Ee zJD~%=RL}PReSthEUC7sw)lb`X6&CLO5&it}tIYcjFAy$4Y08bg{zbH16S(m8^{;Tz zknPj7Q#;RCf%;-A@zuUnfkp^`Hasysa9#M@zs=A+H&oaGUE4w%%ws_L9m~I_?}Oal zb17He?@g$`ymkWwcqkd_uU!|&aBW>%`*Q$(7sE_P$0)GtaMezDFavNv@UxV2TSo$4 zR7TF9x%2!G97va6n3umQIKQ~yd&8I;My-8#LotWkdyP5Fba!!ie{3|ebFM2TR)1mi z-h^)DL`jy`iL}j6_~#TI)icJDI!h1Y?4sXVhl%Wqx5UPmdf z4Em9}5w393=xxPopF2(lI%*F0JaU>L6SL-0nd6Eb1DSzRYt*1>L!LDB@o|AkD^};b zOkdG`*^e4)H%t$vjZzs;g+IjP ze0Td3&uD!ZLeVzl>P;6bU45B_oN}8;na_T)oI%LlkexiLC={-w^%vV039=L{j0HU= z3HNoeUlOypi2Nq6(mLBvnvZGFEIPAT?kD;v`bJSSSI1eC;y4-DoyL1E`2GnUzgHV_ zt4U|^$B#5^oFE@HM9xHVZhX=AwZlkAM_Tou%^%Y;z7{JYZV+ECb`)G#Doy%i;j8TC2Z8t#wvo&XhbT_TnZIP73;a( zsED^Sx>!lH;|a4YMJhX~C%q%3d`!s}f=gVv#mu5@Eu>yPf1;r@D6L+++EE&*gW9ao zcIgk@o{dwa=DZ4@eDZh9eW^RlJ*Sb%>pEzpH3LlwFk1?ARYEJo>}-;vj#k8^Id{r8 z5Svn7JX`x2fB7=UdJsPgaP!!I>VoS14$zNQ|7ez`m<_e3xevq<5k3*g2?KO%1YQ4U zQG^N^_UC@W3gpK#{JF)fFrxcr!I5QWD@FMfS~2PPW10~o2UGrzLS;Xc=KBtUUulBY|Yy|OQ)0pr=A#O~f~NT{b(5A-Zw z$W-Jei*2oY32R7^>`rUmldb9Spq-K7-swq?Y9W=rR-NdAn`ixPw-f7TTHuVf>Q&EZ znd|Oe*kQ8C_`FK%Z$Ddo_?r0Yjt7yT{%vkOFuvw>Gck@~!L{Za7)5cx20H&p3{a4Us-C`rOEHO$hQd0TlJUzO{!SAcJGH+t{~~6=&`i>$}{RK$bq!( zc{Q##J{5AYoV~Pag}zR@OgD}!!e=j4pWNEgEX zrmqh-rC)x*)|8j8ObF#MJpRGWfGfwwd&+tuuU%#Ys&bf1`KEOE9D4!0fo_RfJ@lPu zDtijH`%-%&R!pkhPHO*glV;DcM*OqGLl}=XYA557LUQ>VACnq9gxK_5}p#D`} zi=d$pzeIu==Dr~+pu1|@y@N~X_tTOeZ@Vo9HD}tX^@D{*I^36zOZ-Aq9p3oa^4#0< zoY564)=bP=O_<7SM0(H9<;JQzY&o2jqS$<_i5?waeY?EAozAvrGYH-7`h(Vhi|_mp z-Ml84*Q|VrWXfY<$DZVpXRp+IkR9}IOfp=TNotZx|OcLL1oE@ z{qu$JAD$0`Gql`*W-E(ndkQ2NN{Ty;!r0MDYpsoxs!T;r6@O3xaF$|7Y@5#Bfd1n$ zL6Kd398ToXa_4Q;NI;^&!v#~1@sR3M&%{!4qqr zkHN0)MOLC@Tvn4!npw0Eu2lF#m<^5QNaC2RD+#zQcde(VM``#7?8M;{+-r~5&me&O z2?)w-+okN2E#`+-)m{hOSQ2h*g`vl&=vtHp1i(ek+tY`cmMBCo2MoaRXH|rU`UiN0 zZL)KZ-=5G3YWB)_Sh=0FD_}W5CI_LGC*Ps z|Bt0PoUtEOHC^{El{fj8-qpD_HXJX23uZ?zj%{5YCmYJ%aKxJbfcjP7-Y%hx>e$`;7Zz&{sX z_z6>Lv)!He#2{)R*IL-l(H*K|ZqEITPB~o*j)^bs=~=;4o-VF+sUg-D3yR-EN>ATW zm1Y;Y@%lolASPdyrH&sP>OA)HJEXGH->i9?~bv9cO@x%;i$H%f!@?VCZ8j z&604$@m+UG`|_|nmS89&>T`98(G^|^a}(JMTb<&~%#uaf+xj~>PH*Noue)xgug1|5 zEcj~jR9-#0X;xCqm%zc(SIV{@fMHgVT&`P4O2b&~&2g)3R|_`{zG>j6e8dk_N;nNGjC{CP7X|>k0jz*>aGePRc+mZCo67$n hY5=HR3u0jh424o;Git7;9ef!88mij&Ki##&{1;d>`o{nO literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/defaults_options.png b/doc/en/user/source/webadmin/tilecache/img/defaults_options.png new file mode 100644 index 0000000000000000000000000000000000000000..e055f298421acd97d991f50030e020e059280b78 GIT binary patch literal 4990 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 Uq*KW1=z{}fqz}<6)p3meFLHo4hyVZp literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/defaults_services.png b/doc/en/user/source/webadmin/tilecache/img/defaults_services.png new file mode 100644 index 0000000000000000000000000000000000000000..3d4e3ffde44be907c0bf9280261fe4453838c67e GIT binary patch literal 5274 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 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/diskquota.png b/doc/en/user/source/webadmin/tilecache/img/diskquota.png new file mode 100644 index 0000000000000000000000000000000000000000..67cb9871460d520ff9bb8d8eba7b020e8f6482bc GIT binary patch literal 9534 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 U)&deG0L-H#rzTr2ZT9Z}09v@vF#rGn literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/editgridset.png b/doc/en/user/source/webadmin/tilecache/img/editgridset.png new file mode 100644 index 0000000000000000000000000000000000000000..8a10161b35b74f2e841160eed3ac382ab917ba53 GIT binary patch literal 20904 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 Xts_rI_R|&J0FaST6fYLl_xk?;Cd#!V literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/gridsets_matrix.png b/doc/en/user/source/webadmin/tilecache/img/gridsets_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..c09115f545ec074497fdfeb15d1c85164ea645c5 GIT binary patch literal 9327 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 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/menu.png b/doc/en/user/source/webadmin/tilecache/img/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..76f47011651a2faadf2874584110a1b316d1a493 GIT binary patch literal 3017 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 LNkvXXu0mjf`maJt literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/newcachedlayer.png b/doc/en/user/source/webadmin/tilecache/img/newcachedlayer.png new file mode 100644 index 0000000000000000000000000000000000000000..8b31f826939520da9fb7c12bae6d735a06f7f263 GIT binary patch literal 8832 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 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/readonlygridset.png b/doc/en/user/source/webadmin/tilecache/img/readonlygridset.png new file mode 100644 index 0000000000000000000000000000000000000000..7b524649b6b0f3c756c4fb32b9b571c94e27ae48 GIT binary patch literal 4200 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 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/img/tilelayers.png b/doc/en/user/source/webadmin/tilecache/img/tilelayers.png new file mode 100644 index 0000000000000000000000000000000000000000..37528a685052a8562e71df19caa9b1b05ffdcada GIT binary patch literal 17804 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 u_` pages. To do this, just click on the layer name itself. + From c90b48cf043be97b548e503517c6a6285ac3c099 Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Fri, 3 Aug 2012 15:48:56 -0700 Subject: [PATCH 13/72] Removing unecessary blank file --- doc/en/user/source/webadmin/tilecache/quotas.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/en/user/source/webadmin/tilecache/quotas.rst diff --git a/doc/en/user/source/webadmin/tilecache/quotas.rst b/doc/en/user/source/webadmin/tilecache/quotas.rst deleted file mode 100644 index e69de29bb2d..00000000000 From 906dfa46f20418978c73e9e41ad70fef06ae8b8d Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Fri, 3 Aug 2012 15:50:15 -0700 Subject: [PATCH 14/72] Hiding todos and copyright year in user docs config file --- doc/en/user/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/user/source/conf.py b/doc/en/user/source/conf.py index 3a409461d94..263da6addf9 100644 --- a/doc/en/user/source/conf.py +++ b/doc/en/user/source/conf.py @@ -25,7 +25,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.todo'] -todo_include_todos = True +#todo_include_todos = True # Add any paths that contain templates here, relative to this directory. #templates_path = ['../../theme/_templates'] @@ -39,7 +39,7 @@ # General substitutions. project = u'GeoServer' manual = u'User Manual' -copyright = u'2011 GeoServer' +copyright = u'GeoServer' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. From 07b8dedca13a49d6ec0beb70bc746b2d42a4d343 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sat, 4 Aug 2012 15:45:41 +0200 Subject: [PATCH 15/72] Adding security documentation --- doc/en/user/source/security/passwd.rst | 40 +++++++++++++------ .../security/usergrouprole/interaction.rst | 24 +++++++---- .../source/security/usergrouprole/roles.rst | 9 ++++- .../security/usergrouprole/usergroups.rst | 11 +++-- .../usergrouprole/usergroupservices.rst | 10 ++--- 5 files changed, 63 insertions(+), 31 deletions(-) diff --git a/doc/en/user/source/security/passwd.rst b/doc/en/user/source/security/passwd.rst index a5744107416..33952e19a78 100644 --- a/doc/en/user/source/security/passwd.rst +++ b/doc/en/user/source/security/passwd.rst @@ -15,32 +15,48 @@ A GeoServer configuration stores two types of passwords: * Passwords for **user accounts** that are used to access GeoServer resources * Passwords used internally for **accessing external services** such as databases and cascading OGC services -As these passwords are typically stored on disk it is strongly recommended that they be encrypted and not stored in human-readable text. GeoServer security provides three schemes for encrypting passwords: **plain text**, **Digest**, and **Password-based encryption (PBE)**. +As these passwords are typically stored on disk it is strongly recommended that they be encrypted and not stored in human-readable text. GeoServer security provides four schemes for encrypting passwords: **empty**, **plain text**, **Digest**, and **Password-based encryption (PBE)**. -The password encryption scheme is specified in the following places: a global setting that affects the encryption of passwords used for external resources, and for each :ref:`user/group service `. The encryption scheme for external resources should be :ref:`reversible `, while the user/group services can use any scheme. +The password encryption scheme is specified in the following places: a global setting that affects the encryption of passwords used for external resources, and for each :ref:`user/group service `. The encryption scheme for external resources has to be be :ref:`reversible `, while the user/group services can use any scheme. + +Empty +~~~~~ + +The scheme is not reversible. Any password is encoded as an empty string, as a consequence it is not possible to recalculate the plain text password. This scheme is used for user/group services in combination with an authentication mechanism using a back end system. Examples are user name/password authentication against a LDAP server or a JDBC database. In these scenarios, storing passwords locally to Geoserver does not make sense. Plain text ~~~~~~~~~~ .. note:: Prior to version 2.2.0, plain text encryption was the only available method used by GeoServer for storing passwords. -Plain text passwords provide no encryption at all. In this case, passwords are human-readable by anyone who has access to the file system. For obvious reasons, this is not recommended for any but the most basic test server. +Plain text passwords provide no encryption at all. In this case, passwords are human-readable by anyone who has access to the file system. For obvious reasons, this is not recommended for any but the most basic test server. A password ``mypassword`` is encoded as ``plain:mypassword``, the prefix uniquely describes the algorithm used for encoding/decoding. Digest ~~~~~~ -Digest encryption applies a SHA-256 `cryptographic hash function `_ -to passwords. This scheme is "one-way" in that it is virtually impossible to reverse and obtain the original password from +Digest encryption is not reversible. It applies a SHA-256 `cryptographic hash function `_ +to passwords 100000 times iteratively. This scheme is "one-way" in that it is virtually impossible to reverse and obtain the original password from its hashed representation. Please see the section on :ref:`sec_passwd_reversible` for more information on reversibility. +In order to protect from well known attacks, a random value called a `salt `_ is added to the password when generating the key. For each digesting, a separate random salt is used. Digesting the same password twice results in different hashed representations. + +As an example, the password ``geoserver`` is digested to ``digest1:YgaweuS60t+mJNobGlf9hzUC6g7gGTtPEu0TlnUxFlv0fYtBuTsQDzZcBM4AfZHd``. +``digest1`` indicates the usage of digesting. The hashed representation and the salt are base 64 encoded. + Password-based encryption ~~~~~~~~~~~~~~~~~~~~~~~~~ -`Password-based encryption `_ (PBE) is an encryption scheme that employs a user-supplied passphrase to generate an encryption key. In order to protect from dictionary attacks, a random value called a `salt `_ is added to the passphrase when generating the key. +`Password-based encryption `_ (PBE) is an encryption scheme that normally employs a user-supplied password to generate an encryption key. This scheme is reversible. A random salt described in the previous chapter is used. -GeoServer comes with support for two forms of PBE. **Weak PBE** (the GeoServer default) uses a basic encryption method that is relatively easy to crack. **Strong PBE** uses a much stronger encryption method based on an AES 256-bit algorithm. It is highly recommended to use Strong PBE. +.. note:: The system never uses passwords specified by users because these passwords tend to be week. Passwords used for encryption are generated using a secure random generator and stored in the GeoServer key store. The number of possible passwords is 2^260 . -.. warning:: JUSTIN-TODO: BE MORE SPECIFIC ABOUT ALGORITHM! +GeoServer comes with support for two forms of PBE. **Weak PBE** (the GeoServer default) uses a basic encryption method that is relatively easy to crack. The encryption key is derived from the password using `MD5 `_ 1000 times iteratively. The encryption algorithm itself is `DES `_ (Data Encryption Standard). DES has an effective key length of 56 bits, this is not really a challenge for computer systems in these days. + + +**Strong PBE** uses a much stronger encryption method based on an `AES `_ 256-bit algorithm with `CBC `_. They key length is 256 bit and is derived using `SHA-256 `_ instead of MD5. It is highly recommended to use Strong PBE. + +As an example, the password ``geoserver`` is encrypted to ``crypt1:KWhO7jrTz/Gi0oTQRKsVeCmWIZY5VZaD``. +``crypt1`` indicates the usage of Weak PBE. The prefix for Strong PBE is ``crypt2``. The ciphertext and the salt is base 64 encoded. .. _sec_passwd_encryption_policies: @@ -51,8 +67,6 @@ GeoServer comes with support for two forms of PBE. **Weak PBE** (the GeoServer * `Oracle JCE policy jars `_ for Oracle JVM * `IBM JCE policy jars `_ for IBM JVM - .. warning:: THIS DIDN'T WORK FOR ME. - .. _sec_passwd_reversible: @@ -61,14 +75,14 @@ Reversible encryption Password encryption methods can be **reversible**, meaning that it is possible (and desired) to obtain the plain-text password from its encrypted version. Reversible passwords are necessary for database connections or external OGC services such as :ref:`cascading WMS ` and :ref:`cascading WFS `, since GeoServer must be able to decode the encrypted password and pass it to the external service. Plain text and PBE passwords are reversible. -Non-reversible passwords provide the highest level of security, and therefore should be used for user accounts and wherever else it is possible. Digest is the only encryption scheme that is non-reversible. +Non-reversible passwords provide the highest level of security, and therefore should be used for user accounts and wherever else it is possible. Using password digesting is highly recommended, the installation of the unrestricted policy files is not required. .. _sec_passwd_keystore: Secret keys and the keystore ---------------------------- -For a reversible password to provide a meaningful level of security, access to the password must be restricted in some way. In GeoServer, encrypting and decrypting passwords involves the generation of secret (private) keys, stored in a typical Java *keystore*. GeoServer uses its own keystore for this purpose named ``geoserver.jceks`` which is located in the ``security`` directory in the GeoServer data directory. This file is stored in the `JCEKS format rather than the default JKS `_. +For a reversible password to provide a meaningful level of security, access to the password must be restricted in some way. In GeoServer, encrypting and decrypting passwords involves the generation of secret shared keys, stored in a typical Java *keystore*. GeoServer uses its own keystore for this purpose named ``geoserver.jceks`` which is located in the ``security`` directory in the GeoServer data directory. This file is stored in the `JCEKS format rather than the default JKS `_. JKS does not support storing shared keys. The GeoServer keystore is password protected with a :ref:`sec_master_passwd`. It is possible to access the contents of the keystore with external tools such as `keytool `_. For example, this following command would prompt for the master password and list the contents of the keystore: @@ -102,7 +116,7 @@ Password policies A password policy defines constraints on passwords such as password length, case, and required mix of character classes. Password policies are specified when adding :ref:`sec_rolesystem_usergroupservices` and used to constrain passwords when creating new users and when changing passwords of existing users. -Each user/group service uses a password policy to enforce these rules. The default GeoServer password policy allows the following optional constraints: +Each user/group service uses a password policy to enforce these rules. The default GeoServer password policy implementation allows the following optional constraints: * Passwords must contain at least one number * Passwords must contain at least one upper case letter diff --git a/doc/en/user/source/security/usergrouprole/interaction.rst b/doc/en/user/source/security/usergrouprole/interaction.rst index 7c192a3d6a6..f6b80f3588e 100644 --- a/doc/en/user/source/security/usergrouprole/interaction.rst +++ b/doc/en/user/source/security/usergrouprole/interaction.rst @@ -5,18 +5,28 @@ Interaction between user/group and role services The following information provides some details on the interaction between the :ref:`sec_rolesystem_usergroupservices` and the :ref:`sec_rolesystem_roleservices`. -Providing user information --------------------------- +Calculating the roles of a user +------------------------------- -The following diagram illustrates a user/group service and a role service interacts in order to provide user information. +The following diagram illustrates how a user/group service and a role service interact in order to calculate the roles of a user .. figure:: images/usergrouprole1.png :align: center - *User/group and role service interacting to provide user information* + *User/group and role service interacting for role calculation* + +On fetching an enabled user from a user/group service, his roles have to be calculated. The detailed procedure is described below. + +#. Fetch all enabled groups of the user. If a group is disabled, it is not of interest. +#. Fetch all roles associated to the user and put them into the result set +#. For each enabled group the user is a member, fetch all roles associated with the group and add them to the result set. +#. For each role in the result set, fetch all ancestor roles and add them to the result set. +#. Personalize each role in the result set if necessary +#. If the result set contains the local admin role, add the role ``ROLE_ADMINISTRATOR`` +#. If the result set contains the local group admin role, add the role ``ROLE_GROUP_ADMIN`` + +Personalization of role looks for role parameters (key value pairs) of each role and checks if the user properties (key value pairs) contain and identical key. If this is the case, the value of the role parameter is replaced by the value of the user property. -The user/group service provides the lookup interface for user information. Part of loading information for a user involves -delegating to the role service to determine what roles/authorities the specific user has associated with them. Authentication of user credentials ---------------------------------- @@ -41,4 +51,4 @@ The following figure illustrates the default user/group service, role service, a Two authentication providers are configured. The *Root* provider authenticates for the GeoServer :ref:`sec_root` and does not use a user/group service. The *Username/password* provider is the default provider that simply passes off username and password credentials to a user/group service. -A single user/group service that persists the user database as XML is present. The database contains a single user named ``admin`` and no groups. Similarly, the role server persists the role database as XML. By default this contains a single role named ``ROLE_ADMINISTRATOR``, with the ``admin`` user associated with it. +A single user/group service that persists the user database as XML is present. The database contains a single user named ``admin`` and no groups. Similarly, the role server persists the role database as XML. By default this contains a single role named ``ADMIN``, with the ``admin`` user associated with it. The ``ADMIN`` role is mapped to ``ROLE_ADMINISTRATOR`` role and thus, the ``admin`` user is associated with system administrator role during role calculation. diff --git a/doc/en/user/source/security/usergrouprole/roles.rst b/doc/en/user/source/security/usergrouprole/roles.rst index 32ac163334c..ef9e42a5e0c 100644 --- a/doc/en/user/source/security/usergrouprole/roles.rst +++ b/doc/en/user/source/security/usergrouprole/roles.rst @@ -14,6 +14,11 @@ GeoServer roles support inheritance; a child role inherits all the access grante Key/value pairs are implementation-specific and may be set by the :ref:`role service ` that the user or group originates from. For example, a role service that assigns roles based on employee organization may wish to associate additional information with the role such as Department. -The default role in GeoServer is ``ROLE_ADMINISTRATOR``, which gives access to all operations and resources. +Geoserver has some system roles, the names of these roles are reserved and it is not allowed to add roles with a reserved name. + +* ``ROLE_ADMINISTRATOR`` gives access to all operations and resources. +* ``ROLE_GROUP_ADMIN`` is a special role for administrating user groups. +* ``ROLE_AUTHENTICATED`` is assigned to every user authenticating successfully. +* ``ROLE_ANONYMOUS`` is assigned if anonymous authentication is enabled and user does not log in. + -.. warning:: JUSTIN-TODO: LIST OTHER PSEUDOROLES? \ No newline at end of file diff --git a/doc/en/user/source/security/usergrouprole/usergroups.rst b/doc/en/user/source/security/usergrouprole/usergroups.rst index 1b7c50fcb32..7e25c2f630c 100644 --- a/doc/en/user/source/security/usergrouprole/usergroups.rst +++ b/doc/en/user/source/security/usergrouprole/usergroups.rst @@ -3,17 +3,20 @@ Users and Groups ================ -A GeoServer **user** is defined similarly to most security systems. For each user the following information is maintained: +A GeoServer **user** is defined similarly to most security systems. The correct Java term is **principal**. A principal may be a human being, a software system, a computer and so on. In this introduction, the term **user** is used. For each user the following information is maintained: * User name * :ref:`Password ` (optionally stored :ref:`encrypted `) +* A flag indicating if the user is enabled (this is the default). A disabled user is not allowed to log in in the future. Existing log ins are not affected. * Set of key/value pairs +Key/value pairs are implementation-specific and may be set by the :ref:`user/group service ` that the user or group +originates from. For example, a user/group service that maintains information about a user such as Name, Email address, etc., may wish to associate those data values with the user object. + A GeoServer **group** is simply a set of users. For each group the following information is maintained: * Group name +* A flag indicating if the group is enabled (this is the default). A disabled group does not contribute to the role calculation for all users contained in this group. * List of users that are members of the group -* Set of key/value pairs -Key/value pairs are implementation-specific and may be set by the :ref:`user/group service ` that the user or group -originates from. For example, a user/group service that maintains information about a user such as Name, Email address, etc., may wish to associate those data values with the user object. + diff --git a/doc/en/user/source/security/usergrouprole/usergroupservices.rst b/doc/en/user/source/security/usergrouprole/usergroupservices.rst index 6d7aac72616..f7cfc43228b 100644 --- a/doc/en/user/source/security/usergrouprole/usergroupservices.rst +++ b/doc/en/user/source/security/usergrouprole/usergroupservices.rst @@ -67,7 +67,7 @@ The JDBC user/group service persists the user/group database via JDBC. It repre - NO - PRI * - password - - varchar(64) + - varchar(254) - YES - * - enabled @@ -133,7 +133,7 @@ The JDBC user/group service persists the user/group database via JDBC. It repre The ``users`` table is the primary table and contains the list of users with associated passwords. The ``user_props`` table is a mapping table that maps additional properties to a user. (See :ref:`sec_rolesystem_usergroups` for more details.) The ``groups`` table lists all available groups, and the ``group_members`` table contains the mapping of users to the groups they are associated with. -The default GeoServer security configuration would be represented with the following database contents: +The default GeoServer security configuration would be represented by having all tables empty: .. list-table:: Table: users :widths: 15 15 15 @@ -142,9 +142,9 @@ The default GeoServer security configuration would be represented with the follo * - name - password - enabled - * - ``admin`` - - ``digest1:UTb...`` - - ``Y`` + * - *Empty* + - *Empty* + - *Empty* .. list-table:: Table: user_props :widths: 15 15 15 From 3a58bd870caca6b83cb79b8048d51fae25bd6ee0 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 4 Aug 2012 15:59:57 +0200 Subject: [PATCH 16/72] GSIP77, dev documentation changes --- doc/en/developer/source/index.rst | 1 + .../source/time-boxed-releases/index.rst | 105 ++++++++++++++++++ .../source/time-boxed-releases/timeboxed.png | Bin 0 -> 15496 bytes 3 files changed, 106 insertions(+) create mode 100644 doc/en/developer/source/time-boxed-releases/index.rst create mode 100644 doc/en/developer/source/time-boxed-releases/timeboxed.png diff --git a/doc/en/developer/source/index.rst b/doc/en/developer/source/index.rst index 29f2a0cbd84..7f0f5758fe6 100644 --- a/doc/en/developer/source/index.rst +++ b/doc/en/developer/source/index.rst @@ -14,6 +14,7 @@ Welcome to the GeoServer Developer Manual. The manual is for those who want to eclipse-guide/index findbugs-guide/index programming-guide/index + time-boxed-releases/index release-guide/index release-testing-checklist/index cite-test-guide/index diff --git a/doc/en/developer/source/time-boxed-releases/index.rst b/doc/en/developer/source/time-boxed-releases/index.rst new file mode 100644 index 00000000000..98ef6d34c5d --- /dev/null +++ b/doc/en/developer/source/time-boxed-releases/index.rst @@ -0,0 +1,105 @@ +.. _time_boxed_releases: + +Time boxed releases +=================== + +The release model, starting with GeoServer 2.2.0, is based on time boxing, that is, a setup in which +the software is released: +* at predictable dates with whatever +* with whatever fix/improvements are available at the time + +To compensate the eventual unpredictability of the release contents the model includes strict rules +about what might be committed on each branch, and a suitably long hardening period in which the +unstable series gets stabilized. + +Release timings +--------------- + +The cycle is based on: +* monthly releases on the stable series +* a four month open development period followed by two months hardening period +* beta releases are supposed to be released out of the unstable series on a monthly basis + across the switch between open development and hardening, followed by the first RC +* RC are pushed out every two weeks until we reach a stable code base, which will be released + as the new major stable release +* the first RC marks the branch off of a new trunk, on which open development starts again + +The following picture exemplifies the cycle: + + .. image:: timeboxed.png + +Every month, on the same day, a new release is issued using whatever revision of GeoServer/Geotools passed the last CITE tests. +The release is meant to improve upon the previous release in both functionality and stability, so unless +the project steering committee determines reasons to block the release it will happen regardless of what bug +reports are in Jira (pending resourcing, they can be fixed in the next release that comes out one month later). + +At every point in time there are two branches, a stable branch and a trunk, with just one month every +six where there are three active branches (nothing prevents developers willing to keep the stable series +up longer working there if they wish to, it's just not expected anymore). + +The three phases +---------------- + +Stable branch +````````````` + +The stable branch is meant for bug fixes and new features that do not affect the GeoServer API or +significantly affect the stability. +A PSC vote (with eventual proposal) can be called in case a significant new feature or change needs +to be back ported to the stable branch overriding the above rules. + +If, for any reason, a release is delayed the next release will be rescheduled 30 days after the last release +(that is, delaying the whole training of remaining releases). + +Trunk in open development mode +`````````````````````````````` + +The open development mode starts when the new stable release is branched off, and ends when hardening +starts, four months after the new stable release is made. + +During this operational mode developers are free to commit stability undermining changes (even significant ones). +Those changes still need to be voted as GSIP anyways to ensure, as usual, resourcing checks, API consistency and community review. + +After three months from the release of the stable series a first beta will be released, +one month after that the second beta will be released and the trunk will switch into hardening mode. + +Trunk in hardening mode +``````````````````````` + +The harderning mode starts when the second beta is released and continuous through all release candidate +releases. The first RC is released one month after the second beta, and then bi-weekly releases +will be issued until no major issues will be reported by the user base, at which point the last RC +will be turned into the new stable release. + +During harderning only bug-fixes and new non core plugins can be developed + +Commit rules +------------ + +While the PSC is going to be able to vote and override the committing guidelines, it is still good +to have some reference and default of what can be done or not done in the various branches. + +**Hardening mode**, and by extension, stable and trunk, can take any of the following: + +* bug fixes +* documentation improvements +* new plugins contributed as community or extension modules (with a cautionary note that during + hardening the attention should be concentrated as much as possible on getting the new release stable) + + +**Stable branch** can take in addition the following: + +* new minor core functionality (e.g. new output format, +* new API that we can commit to for a long period of time (provided it's not a change to existing API unless the PSC votes otherwise). + GeoServer is not a library, so defining API can be hard, but any class that can be used by pluggable + extension points should be changed with care, especially so in a stable series + +In addition to the above the **stable branch** can get the following changes provided there is +a **solid PSC/PMC vote** (e.g., no doubts or concerns about them) to include them: + +* promotion of extensions to core +* core changes that are unlikely to affect the stability of the upcoming release + (if the PSC is ok better land them right after a release to get as a large window for testing as possible) +* backport of larger changes that have proven to be working well on trunk for an extended period of time + +**Trunk** in open development mode can take everything, of course large changes are still subject to proposals and reviews. \ No newline at end of file diff --git a/doc/en/developer/source/time-boxed-releases/timeboxed.png b/doc/en/developer/source/time-boxed-releases/timeboxed.png new file mode 100644 index 0000000000000000000000000000000000000000..bfab21f81b33a36bd15393e1108b10553f010ab7 GIT binary patch literal 15496 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 OM@n2CRPs*W@BaYL94R*d literal 0 HcmV?d00001 From 4aa4016cf8fef63150758c420a4cb40e4a856127 Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Mon, 6 Aug 2012 11:19:29 -0700 Subject: [PATCH 17/72] Resize to fit images in docs --- doc/en/themes/geoserver/static/default.css | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/themes/geoserver/static/default.css b/doc/en/themes/geoserver/static/default.css index 80d41b1862e..32a47799e10 100644 --- a/doc/en/themes/geoserver/static/default.css +++ b/doc/en/themes/geoserver/static/default.css @@ -359,6 +359,7 @@ border-bottom: 0 none transparent; #content img { margin-bottom: 10px; +max-width:99%; } #content .figure img { From 875084434a3ea8255ab942c390f56191293bef08 Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Mon, 6 Aug 2012 16:53:40 -0700 Subject: [PATCH 18/72] More details about the improved GWC integration --- doc/en/user/source/geowebcache/config.rst | 39 +++-- doc/en/user/source/geowebcache/demopage.rst | 37 ----- .../geowebcache/images/geowebcache-reload.png | Bin 10227 -> 0 bytes .../source/geowebcache/images/geowebcache.png | Bin 34539 -> 0 bytes doc/en/user/source/geowebcache/index.rst | 6 +- .../source/geowebcache/responseheaders.rst | 58 +++++++ doc/en/user/source/geowebcache/seeding.rst | 39 +---- .../source/geowebcache/troubleshooting.rst | 141 +++++++++++++++++- doc/en/user/source/geowebcache/using.rst | 64 ++++++-- .../tutorials/superoverlaysgwc.rst | 2 +- .../source/webadmin/tilecache/defaults.rst | 4 +- .../source/webadmin/tilecache/demopage.rst | 75 ++++++++++ .../source/webadmin/tilecache/gridsets.rst | 2 +- .../webadmin/tilecache/img/demopage.png | Bin 0 -> 18032 bytes .../tilecache/img/demopage_reload.png | Bin 0 -> 8694 bytes .../user/source/webadmin/tilecache/index.rst | 3 +- .../user/source/webadmin/tilecache/layers.rst | 4 +- 17 files changed, 368 insertions(+), 106 deletions(-) delete mode 100644 doc/en/user/source/geowebcache/demopage.rst delete mode 100644 doc/en/user/source/geowebcache/images/geowebcache-reload.png delete mode 100644 doc/en/user/source/geowebcache/images/geowebcache.png create mode 100644 doc/en/user/source/geowebcache/responseheaders.rst create mode 100644 doc/en/user/source/webadmin/tilecache/demopage.rst create mode 100644 doc/en/user/source/webadmin/tilecache/img/demopage.png create mode 100644 doc/en/user/source/webadmin/tilecache/img/demopage_reload.png diff --git a/doc/en/user/source/geowebcache/config.rst b/doc/en/user/source/geowebcache/config.rst index 4907d0db917..f7f4c973bc2 100644 --- a/doc/en/user/source/geowebcache/config.rst +++ b/doc/en/user/source/geowebcache/config.rst @@ -1,13 +1,38 @@ .. _gwc_config: -GeoWebCache Configuration -========================= +Configuration +============= GeoWebCache is automatically configured to be used with GeoServer with the most common options, with **no setup required**. All communication between GeoServer and GeoWebCache happens by passing messages inside the JVM. -By default, all layers served by GeoServer will be known to GeoWebCache. See the :ref:`gwc_demo` page to test the configuration. +By default, all layers served by GeoServer will be known to GeoWebCache. See the :ref:`webadmin_tilecaching_layers` page to test the configuration. + +.. note:: Configuration of the integrated GeoWebCache changed significantly as of version 2.2.0. + +Integrated user interface +------------------------- + +GeoWebCache has a full integrated web-based configuration. See the :ref:`webadmin_tilecaching` section in the :ref:`web_admin`. + +Determining tiled layers +------------------------ + +In versions of GeoServer priori to 2.2.0, the GeoWebCache integration was done in a such way that every GeoServer layer and layer group was forced to have an associated GeoWebCache tile layer. In addition, every such tile layer was forcedly published in the EPSG:900913 and EPSG:4326 gridsets with PNG and JPEG output formats. + +Now, it is possible to selectively turn caching on or off for any layer served through GeoServer. This setting can be done on the :ref:`webadmin_tilecaching_layers` section in the :ref:`web_admin`. + +Configuration files +------------------- + + +It is possible to configure most aspects of cached layers through the :ref:`webadmin_tilecaching` section in the :ref:`web_admin`. + +GeoWebCache keeps the configuration for each GeoServer tiled layer separately, inside the :file:`/gwc-layers/` directory. There is one XML file for each tile layer. These files contain a different syntax from the ```` syntax in the standalone version and are *not* meant to be edited by hand. Instead you can configure tile layers on the :ref:`webadmin_tilecaching_layers` page. + +Configuration for the defined gridsets still is saved in :file:`/gwc/geowebcache.xml`` so that the integrated GeoWebCache can continue to serve externally-defined tile layers from WMS services outside GeoServer. + +If upgrading from a version prior to 2.2.0, a migration process is run which creates a tile layer configuration for all the available layers and layer groups in GeoServer with the old defaults. From that point on, you should configure the tile layers on the :ref:`webadmin_tilecaching_layers` page. -.. note:: The ``GEOSERVER_WMS_URL`` parameter in :file:`web.xml`, used in earlier versions of GeoServer, is deprecated and should not be used. Changing the cache directory ---------------------------- @@ -25,13 +50,9 @@ Change the path inside ```` to the desired cache path (such as :fil .. note:: Make sure GeoServer has write access in this directory. -Custom configuration --------------------- - -If you need to access more features than the automatic configuration offers, you can create a custom configuration file. Inside the GeoWebCache cache directory (see above), create a file named :file:`geowebcache.xml`. Please refer to the `GeoWebCache documentation `_ for how to customize this file. Restart GeoServer for the changes to take effect. You may also wish to check the logfiles after starting GeoServer to verify that this file has been successfully read. - GeoWebCache with multiple GeoServer instances --------------------------------------------- For stability reasons, it is not recommended to use the embedded GeoWebCache with multiple GeoServer instances. If you want configure GeoWebCache as a front-end for multiple instances of GeoServer, we recommend using the `standalone GeoWebCache `_. + diff --git a/doc/en/user/source/geowebcache/demopage.rst b/doc/en/user/source/geowebcache/demopage.rst deleted file mode 100644 index 8e153749e28..00000000000 --- a/doc/en/user/source/geowebcache/demopage.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _gwc_demo: - -GeoWebCache Demo page -===================== - -GeoWebCache comes with a demo page where you can view configured layers, reload the configuration (when changing settings or adding new layers), and seed/refresh the existing cache on a per-layer basis. - -.. figure:: images/geowebcache.png - :align: center - -Viewing -------- - -To view the GeoWebCache demo page, append ``/gwc/demo`` to the address of your GeoServer instance. For example, if your GeoServer is at the following address:: - - http://localhost:8080/geoserver - -The GeoWebCache demo page is accessible here:: - - http://localhost:8080/geoserver/gwc/demo - -If there is a problem loading this page, GeoWebCache may be set up incorrectly. Verify the steps on the :ref:`gwc_using` page have been carried out successfully. - -Reload configuration --------------------- - -The demo page contains a list of every layer that GeoWebCache is aware of. This is typically (though not necessarily) identical to the list of layers as published in the GeoServer WMS capabilities document. If configuration changes are made to GeoServer, GeoWebCache will not automatically become aware of them. To ensure that GeoWebCache is using the latest configuration information, click the **Reload Configuration** button. Reloading the configuration will trigger authentication to GeoServer, and will require an administration username and password. Use the same username and password that you would use to log in to the :ref:`web_admin`. (See :ref:`webadmin_basics` for more information.) After a successful login, the number of layers found and loaded will be displayed. - -.. figure:: images/geowebcache-reload.png - :align: center - -Layers and output formats -------------------------- - -For each layer that GeoWebCache serves, links are typically available for a number of different projections and output formats. By default, **OpenLayers** applications are available using image formats of PNG, PNG8, GIF, and JPEG in both **EPSG:4326** (standard lat/lon) and **EPSG:900913** (used in Google Maps) projections. In addition, **KML output** is available (EPSG:4326 only) using the same image formats, plus vector data ("kml"). - -Also on the list is an option to seed the layers (:guilabel:`Seed this layer`). More on this option can be found on the :ref:`gwc_seeding` page. \ No newline at end of file diff --git a/doc/en/user/source/geowebcache/images/geowebcache-reload.png b/doc/en/user/source/geowebcache/images/geowebcache-reload.png deleted file mode 100644 index 52818fc4c1d4c4290229a2ebd52f502a96422e07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10227 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 diff --git a/doc/en/user/source/geowebcache/images/geowebcache.png b/doc/en/user/source/geowebcache/images/geowebcache.png deleted file mode 100644 index 6271094f4884ebd264b56979393d551b00b8a22d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34539 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 for use with other map servers. +GeoWebCache is a tiling server. It runs as a proxy between a map client and map server, caching (storing) tiles as they are requested, eliminating redundant request processing and thus saving large amounts of processing time. GeoWebCache is integrated with GeoServer, though it is also available as a standalone product for use with other map servers. -This section will discuss the version of GeoWebCache embedded in GeoServer. For information about the standalone product, please see the `GeoWebCache homepage `_. +This section will discuss the version of GeoWebCache integrated with GeoServer. For information about the standalone product, please see the `GeoWebCache homepage `_. .. toctree:: :maxdepth: 2 using config - demopage seeding + responseheaders rest/index troubleshooting diff --git a/doc/en/user/source/geowebcache/responseheaders.rst b/doc/en/user/source/geowebcache/responseheaders.rst new file mode 100644 index 00000000000..30e34f05a34 --- /dev/null +++ b/doc/en/user/source/geowebcache/responseheaders.rst @@ -0,0 +1,58 @@ +.. _gwc_responseheaders: + +HTTP Response Headers +===================== + +GeoWebCache returns both standard and custom HTTP response headers when serving a tile request, both to adhere to an HTTP 1.1 transfer control mechanism and to aid in debugging problems. + +The response headers can be determined via a utility such as `cURL `_. + +Example +------- + + +This is a sample request and response using cURL:: + + curl -v "http://localhost:8080/geowebcache/service/wms?LAYERS=sde%3Abmworld&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=-180,-38,-52,90&WIDTH=256&HEIGHT=256&tiled=true" > /dev/null + +:: + + < HTTP/1.1 200 OK + < geowebcache-tile-index: [0, 1, 2] + < geowebcache-cache-result: HIT + < geowebcache-tile-index: [0, 1, 2] + < geowebcache-tile-bounds: -180.0,-38.0,-52.0,90.0 + < geowebcache-gridset: GlobalCRS84Pixel + < geowebcache-crs: EPSG:4326 + < Content-Type: image/png + < Content-Length: 102860 + < Server: Jetty(6.1.8) + +From this, one can learn that the tile was found in the cache (``HIT``), the requested tile was from the gridset called ``GlobalCRS84Pixel`` and had a CRS of ``EPSG:4326``. + + +Full list of response headers +----------------------------- + +The following is the full list of response headers. Whenever GeoWebCache serves a tile request, it will write some or all of the following custom headers on the HTTP response. + +.. list-table:: + :header-rows: 1 + + * - Response Header + - Description + * - ``geowebcache-cache-result`` + - Shows whether the GeoWebCache WMS was used. Options are: + + * ``HIT``: Tile requested was found on the cache + * ``MISS``: Tile was not found on the cache but was acquired from the layer's data source + * ``WMS``: Request was proxied directly to the origin WMS (for example, for GetFeatureInfo requests) + * ``OTHER``: Response was the default white/transparent tile or an error occurred + * - ``geowebcache-tile-index`` + - Contains the three-dimensional tile index in x,y,z order of the returned tile image in the corresponding grid space (e.g. ``[1, 0, 0]``) + * - ``geowebcache-tile-bounds`` + - Bounds of the returned tile in the corresponding coordinate reference system (e.g. ``-180,-90,0,90``) + * - ``geowebcache-gridset`` + - Name of the gridset the tile belongs to (see :ref:`webadmin_tilecaching_gridsets` for more information) + * - ``geowebcache-crs`` + - Coordinate reference system code of the matching gridset (e.g. ``EPSG:900913``, ``EPSG:4326``, etc). diff --git a/doc/en/user/source/geowebcache/seeding.rst b/doc/en/user/source/geowebcache/seeding.rst index 2d516e64ce6..cd5f42e7e4c 100644 --- a/doc/en/user/source/geowebcache/seeding.rst +++ b/doc/en/user/source/geowebcache/seeding.rst @@ -5,6 +5,8 @@ Seeding and refreshing The primary benefit to GeoWebCache is that it allows for the acceleration of normal WMS tile request processing by eliminating the need for the tiles to be regenerated for every request. This page discusses tile generation. +You can configure seeding processes via the :ref:`web_admin`. See the :ref:`webadmin_tilecaching_layers` page for more information. + Generating tiles ---------------- @@ -14,40 +16,3 @@ The other way for tiles to be generated is by **seeding**. Seeding is the proce In practice, a combination of both methods are usually used, with certain zoom levels (or certain areas of zoom levels) seeded, and the less-likely-viewed tiles are left uncached. -Seeding options ---------------- - -The :ref:`gwc_demo` contains a link next to each layer entitled :guilabel:`Seed this layer`. This link will trigger authentication with the GeoServer configuration. Use the same username and password that you would use to log in to the :ref:`web_admin`. (See :ref:`webadmin_basics` for more information.) After a successful login, a new page shows up with seeding options. - -The seeding options page contains various parameters for configuring the way that the layer is seeded. - -.. list-table:: - :widths: 20 80 - :header-rows: 1 - - * - Option - - Description - * - ``Number of threads to use`` - - Possible values are between **1** and **16**. - * - ``Type of operation`` - - Sets the operation. There are three possible values: **Seed** (creates tiles, but does not overwrite existing ones), **Reseed** (like Seed, but overwrites existing tiles) and **Truncate** (deletes all tiles within the given parameters) - * - ``SRS`` - - Specifies the projection to use when creating tiles (default values are **EPSG:4326** and **EPSG:900913**) - * - ``Format`` - - Sets the image format of the tiles. Can be **application/vnd.google-earth.kml+xml** (Google Earth KML), **image/gif** (GIF), **image/jpeg** (JPEG), **image/png** (24 bit PNG), and **image/png8** (8 bit PNG) - * - ``Zoom start`` - - Sets the minimum zoom level. Lower values indicate map views that are more zoomed out. When seeding, GeoWebCache will only create tiles for those zoom levels inclusive of this value and ``Zoom stop``. - * - ``Zoom stop`` - - Sets the maximum zoom level. Higher values indicate map views that are more zoomed in. When seeding, GeoWebCache will only create tiles for those zoom levels inclusive of this value and ``Zoom start``. - * - ``Bounding box`` - - *(optional)* Allows seeding to occur over a specified extent, instead of the full extent of the layer. This is useful if your layer contains data over a large area, but the application will only request tiles from a subset of that area. The four boxes correspond to **Xmin**, **Ymin**, **Xmax**, and **Ymax**. - -When finished, click :guilabel:`Submit`. - -.. warning:: Currently there is no progress bar to inform you of the time required to perform the operation, nor is there any intelligent handling of disk space. In short, the process may take a *very* long time, and the cache may fill up your disk. You may wish to set a :ref:`gwc_diskquota` before running a seed job. - -Manually deleting cached content --------------------------------- - -If you have direct access to the file system on the server, you can also delete the appropriate layers in the cache directory. The structure of the cache directory is ``[root]`` / ``layer`` / ``projection_zoomlevel``. - diff --git a/doc/en/user/source/geowebcache/troubleshooting.rst b/doc/en/user/source/geowebcache/troubleshooting.rst index 5cd0be22235..5a10eb4f95a 100644 --- a/doc/en/user/source/geowebcache/troubleshooting.rst +++ b/doc/en/user/source/geowebcache/troubleshooting.rst @@ -3,11 +3,146 @@ Troubleshooting =============== -Sometimes errors will occur when requesting data from GeoWebCache. Below are some of the most common reasons. +This section will discuss some common issues with the integrated GeoWebCache and their solutions. Grid misalignment ----------------- -Sometimes errors will occur saying that the "resolution is not supported" or the "bounds do not align." This is due to the client making WMS requests that do not align with the grid of tiles that GeoWebCache has created, such as differing map bounds or layer bounds, or an unsupported resolution. If you are using OpenLayers as a client, looking at the source code of the included demos may provide more clues to matching up the grid. +Sometimes errors will occur when requesting data from GeoWebCache endpoints. The error displayed might say that the "resolution is not supported" or the "bounds do not align." This is due to the client making WMS requests that do not align with the grid of tiles that GeoWebCache has created, such as differing map bounds or layer bounds, or an unsupported resolution. If you are using OpenLayers as a client, looking at the source code of the included demos may provide more clues to matching up the grid. + +An alternative workaround is to enable direct WMS integration with the GeoServer WMS. You can set this on the :ref:`webadmin_tilecaching_defaults` page. + + +Direct WMS integration +---------------------- + +Direct integration allows WMS requests served through GeoServer to be cached as if they were received and processed by GeoWebCache. With Direct WMS Integration, a request may either be handled by the GeoServer WMS or GeoWebCache WMS. + +Sometimes requests that should go to GeoWebCache will instead be passed through to GeoServer, resulting in no tiles saved. That said, it is possible to determine why a request was not handled by GeoWebCache when intended. This is done by using the command-line utility `cURL `_ and inspecting the response headers. + +First, obtain a sample request. This can easily be done by going to the Layer Preview for a given layer, setting the :guilabel:`Tiled` parameter to :guilabel:`Tiled`, then right-clicking on an area of the map and copy the full path to the image location. If done correctly, the result will be a GET request that looks something like this:: + + http://localhost:8090/geoserver/nurc/wms?LAYERS=nurc%3AArc_Sample&STYLES=&FORMAT=image%2Fjpeg&TILED=true&TILESORIGIN=-180%2C-90&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG%3A4326&BBOX=-45,-45,0,0&WIDTH=256&HEIGHT=256 + +You can then paste this URL into a curl request: + +.. code-block:: console + + curl -v "URL" + +For example: + +.. code-block:: console + + curl -v "http://localhost:8090/geoserver/nurc/wms?LAYERS=nurc%3AArc_Sample&STYLES=&FORMAT=image%2Fjpeg&TILED=true&TILESORIGIN=-180%2C-90&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG%3A4326&BBOX=-45,-45,0,0&WIDTH=256&HEIGHT=256" + +.. note:: To omit the raw image output to the terminal, pipe the output to your system's null. On Linux / OS X, append ``> /dev/null`` to these requests, and on Windows, append ``> nul``. + +If the request doesn't go through GeoWebCache's WMS, a reason will be given in a custom response header. Look for the following response headers: + +* ``geowebcache-cache-result``: Will say ``HIT`` if the GeoWebCache WMS processed the request, and ``MISS`` otherwise. +* ``geowebcache-miss-reason``: If the above shows as ``MISS``, this will generated a short description of why the request wasn't handled by the GeoWebCache WMS. + + +The following are some example requests made along with the responses. These responses have been truncated to show only the information relevant for troubleshooting. + +Successful request +~~~~~~~~~~~~~~~~~~ + +This request was successfully handled by the GeoWebCache WMS. + +Request: + +.. code-block:: console + + curl -v "http://localhost:8080/geoserver/topp/wms?TILED=true&LAYERS=states&FORMAT=image/png&REQUEST=GetMap&STYLES=&SRS=EPSG:4326&BBOX=-135,45,-112.5,67.5&WIDTH=256&HEIGHT=256" + +Response:: + + < HTTP/1.1 200 OK + < Content-Type: image/png + < geowebcache-crs: EPSG:4326 + ... + < geowebcache-layer: topp:states + < geowebcache-gridset: EPSG:4326 + < geowebcache-tile-index: [2, 6, 3] + ... + < geowebcache-cache-result: HIT + < geowebcache-tile-bounds: -135.0,45.0,-112.5,67.5 + ... + +Wrong height parameter +~~~~~~~~~~~~~~~~~~~~~~ + +The following request is not handled by the GeoWebCache WMS because the image requested (256x257) does not conform to the expected 256x256 tile size. + +Request: + +.. code-block:: console + + curl -v "http://localhost:8080/geoserver/topp/wms?TILED=true&LAYERS=states&FORMAT=image/png&REQUEST=GetMap&STYLES=&SRS=EPSG:4326&BBOX=-135,45,-112.5,67.5&WIDTH=256&HEIGHT=257" + +Response:: + + < HTTP/1.1 200 OK + < Content-Type: image/png + < geowebcache-miss-reason: request does not align to grid(s) 'EPSG:4326' + ... + +No tile layer associated +~~~~~~~~~~~~~~~~~~~~~~~~ + +The following request is not handled by the GeoWebCache WMS because the layer requested has no tile layer configured. + +Request: + +.. code-block:: console + + curl -v "http://localhost:8080/geoserver/topp/wms?TILED=true&LAYERS=tasmania_roads&FORMAT=image/png&REQUEST=GetMap&STYLES=&SRS=EPSG:4326&BBOX=-135,45,-112.5,67.5&WIDTH=256&HEIGHT=256" + +Response:: + + < HTTP/1.1 200 OK + < Content-Type: image/png + < geowebcache-miss-reason: not a tile layer + ... + +Missing parameter filter +~~~~~~~~~~~~~~~~~~~~~~~~ + +The following request is not handled by the GeoWebCache WMS because the request contains a parameter filter (BGCOLOR) that is not configured for this layer. + +Request: + +.. code-block:: console + + curl -v "http://localhost:8080/geoserver/topp/wms?BGCOLOR=0xAAAAAA&TILED=true&LAYERS=states&FORMAT=image/png&REQUEST=GetMap&STYLES=&SRS=EPSG:4326&BBOX=-135,45,-112.5,67.5&WIDTH=256&HEIGHT=256" + +Response:: + + < HTTP/1.1 200 OK + < Content-Type: image/png + < geowebcache-miss-reason: no parameter filter exists for BGCOLOR + ... + + +CRS not defined +~~~~~~~~~~~~~~~ + +The following request is not handled by the GeoWebCache WMS because the request references a CRS (EPSG:26986) that does not match any of the tile layer gridsets: + +Request: + +.. code-block:: console + + curl -v "http://localhost:8080/geoserver/topp/wms?TILED=true&LAYERS=states&FORMAT=image/png&REQUEST=GetMap&STYLES=&SRS=EPSG:26986&BBOX=-135,45,-112.5,67.5&WIDTH=256&HEIGHT=256" + +Response:: + + < HTTP/1.1 200 OK + < Content-Type: image/png + < geowebcache-miss-reason: no cache exists for requested CRS + ... + + -An alternative workaround is to set up GeoWebCache integration with the GeoServer WMS. See the section on :ref:`gwc_seeding` for more details. diff --git a/doc/en/user/source/geowebcache/using.rst b/doc/en/user/source/geowebcache/using.rst index 30f4ba98b88..db6c872912d 100644 --- a/doc/en/user/source/geowebcache/using.rst +++ b/doc/en/user/source/geowebcache/using.rst @@ -5,24 +5,62 @@ Using GeoWebCache .. note:: For an more in-depth discussion of using GeoWebCache, please see the `GeoWebCache documentation `_. -GeoWebCache integration with GeoServer WMS ------------------------------------------- -GeoWebCache (as of GeoServer 2.1.0) is transparently integrated with the GeoServer WMS, and so requires no special endpoint or custom URL in order to be used. In this way one can have the simplicity of a standard WMS endpoint with the performance of a tiled client. +Direct integration with GeoServer WMS +------------------------------------- -This direct integration is turned off by default. It can be enabled by going to the :ref:`webadmin_tilecaching` page in the :ref:`web_admin`. +GeoWebCache can be transparently integrated with the GeoServer WMS, and so requires no special endpoint or custom URL in order to be used. In this way one can have the simplicity of a standard WMS endpoint with the performance of a tiled client. -When this feature is enabled, GeoServer WMS will cache and retrieve tiles from GeoWebCache (via a GetMap request) **only if the following conditions apply**: +This direct integration is turned off by default. It can be enabled by going to the :ref:`webadmin_tilecaching_defaults` page in the :ref:`web_admin`. -#. ``TILED=true`` is included in the request. -#. All other request parameters (tile height and width) match up with a tile in the layer's gridset. -#. There are no vendor-specific parameters (such as ``cql_filter``). +When this feature is enabled, GeoServer WMS will cache and retrieve tiles from GeoWebCache (via a GetMap request) only if **all of the following criteria are followed**: -In addition, when direct integration is enabled, the WMS capabilities document (via a GetCapabilities request) will only return the WMS-C vendor-specific capabilities elements (such as a ```` element for each cached layer/CRS/format combination) if ``TILED=true`` is appended to the GetCapabilities request. +* WMS Direct integration is enabled (you can set this on the :ref:`webadmin_tilecaching_defaults` page) +* ``tiled=true`` is included in the request +* The request only references a single layer +* Caching is enabled for that layer +* The image requested is of the same height and width as the size saved in the layer configuration +* The requested CRS matches one of the available tile layer gridsets +* The image requested lines up with the existing grid bounds +* A parameter is included for which there is a corresponding Parameter Filter + +In addition, when direct integration is enabled, the WMS capabilities document (via a GetCapabilities request) will only return the WMS-C vendor-specific capabilities elements (such as a ```` element for each cached layer/CRS/format combination) if ``tiled=true`` is appended to the GetCapabilities request. .. note:: For more information on WMS-C, please see the `WMS Tiling Client Recommendation `_ from OSGeo. -.. note:: GeoWebCache integration is not compatible with the OpenLayers-based :ref:`layerpreview`, as the preview does not usually align with the GeoWebCache layer gridset. This is because the OpenLayers application calculates the tileorigin based on the layer's bounding box, which is different from the gridset. It is, however, very possible to create an OpenLayers application that caches tiles; just make sure that the tileorigin aligns with the gridset. +.. note:: GeoWebCache integration is not compatible with the OpenLayers-based :ref:`layerpreview`, as the preview does not usually align with the GeoWebCache layer gridset. This is because the OpenLayers application calculates the ``tileorigin`` based on the layer's bounding box, which is different from the gridset. It is, possible to create an OpenLayers application that caches tiles; just make sure that the ``tileorigin`` aligns with the gridset. + + +Virtual services +~~~~~~~~~~~~~~~~ + +When direct WMS integration is enabled, GeoWebCache will properly handle requests to :ref:`virtual_services` (``/geoserver//wms?tiled=true&...``). + +Virtual services capabilities documents will contain ```` entries only for the layers that belong to that workspace (and global layer groups), and will be referenced by unqualified layer names (no namespace). For example, the layer ``topp:states`` will be referred to as ``states`` instead of ``topp:states``, and GetMap requests to the virtual services endpoint using ``LAYERS=states`` will properly be handled. + +Supported parameter filters +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With direct WMS integration, the following parameter filters are supported for GetMap requests: + +* ``ANGLE`` +* ``BGCOLOR`` +* ``BUFFER`` +* ``CQL_FILTER`` +* ``ELEVATION`` +* ``ENV`` +* ``FEATUREID`` +* ``FEATUREVERSION`` +* ``FILTER`` +* ``FORMAT_OPTIONS`` +* ``MAXFEATURES`` +* ``PALETTE`` +* ``STARTINDEX`` +* ``TIME`` +* ``VIEWPARAMS`` + +If a request is made using any of the above parameters, the request will be passed to GeoServer, unless a parameter filter has been set up, in which case GeoWebCache will process the request. + GeoWebCache endpoint URL ------------------------ @@ -50,7 +88,9 @@ As soon as tiles are requested through GeoWebCache, GeoWebCache automatically st Disk quota ---------- -GeoWebCache has a built-in disk quota feature to prevent disk space from growing unbounded. Disk quotas are turned off by default, but can be configured on the :ref:`webadmin_tilecaching` page in the :ref:`web_admin`. You can set the maximum size of the cache directory, poll interval, and what policy of tile removal to use when the quota is exceeded. Tiles can be removed based on usage ("Least Frequently Used" or LFU) or timestamp ("Least Recently Used" or LRU). +GeoWebCache has a built-in disk quota feature to prevent disk space from growing unbounded. You can set the maximum size of the cache directory, poll interval, and what policy of tile removal to use when the quota is exceeded. Tiles can be removed based on usage ("Least Frequently Used" or LFU) or timestamp ("Least Recently Used" or LRU). + +Disk quotas are turned off by default, but can be configured on the :ref:`webadmin_tilecaching_diskquotas` page in the :ref:`web_admin`. Integration with external mapping sites --------------------------------------- @@ -65,5 +105,5 @@ The version of GeoWebCache that comes embedded in GeoServer automatically config * **EPSG:4326** (latitude/longitude) * **EPSG:900913** (Spherical Mercator, the projection used in Google Maps) -If you need another projection, you can create a custom configuration file, :file:`geowebcache.xml`, in the same directory that contains the cache (see the :ref:`gwc_config` page for information on how to set this). This configuration file is the same as used by the standalone version of GeoWebCache (see that documentation for more details). The configuration syntax directly supports the most common WMS parameters such as style, palette, and background color. To prevent conflicts, the layers in this file should be named differently from the ones that are loaded from GeoServer. +You can also set a custom CRS from any that GeoServer recognizes. See the :ref:`webadmin_tilecaching_gridsets` page for details. diff --git a/doc/en/user/source/googleearth/tutorials/superoverlaysgwc.rst b/doc/en/user/source/googleearth/tutorials/superoverlaysgwc.rst index 04a356c0f72..b638894007e 100644 --- a/doc/en/user/source/googleearth/tutorials/superoverlaysgwc.rst +++ b/doc/en/user/source/googleearth/tutorials/superoverlaysgwc.rst @@ -16,7 +16,7 @@ Using the GeoWebCache plug in with super-overlays To access GWC from GeoServer go to http://localhost:8080/geoserver/gwc/demo/. This should return a layer list of similar to below. -.. figure:: ../../geowebcache/images/geowebcache.png +.. figure:: ../../webadmin/tilecache/img/demopage.png :align: center To use a super-overlay in GeoWebCache select the KML (vector) option display for each layer. Lets select topp:states.The url would be http://localhost:8080/geoserver/gwc/service/kml/topp:states.kml.kmz diff --git a/doc/en/user/source/webadmin/tilecache/defaults.rst b/doc/en/user/source/webadmin/tilecache/defaults.rst index 2004cbcf68c..68ca5777eda 100644 --- a/doc/en/user/source/webadmin/tilecache/defaults.rst +++ b/doc/en/user/source/webadmin/tilecache/defaults.rst @@ -87,7 +87,9 @@ Default metatile size A metatile is several tiles combined into a larger one. This larger metatile is generated and then sliced up before being served back (and cached) as standard tiles. The advantage of using metatiling is in situations where a label or geometry lies on a boundary of a tile, which might get cut off or altered. With metatiling, these tile edge issues are greatly reduced. -The disadvantage of metatiling is that each WMS rendered tile is much larger, causing a possible performance penalty. Also, at much large sizes, memory consumption can be an issue. +Moreover, with metatiling, the overall time it takes to seed the cache is reduced in most cases, when compared with rendering a full map with single tiles. In fact, using larger metatiling factors is a good way to reduce the time spent in seeding the cache. + +The disadvantage of metatiling is that at large sizes, memory consumption can be an issue. The size of the default metatile can be adjusted here. By default, GeoServer sets a metatile size of **4x4**, which strikes a balance between performance, memory usage, and rendering accuracy. diff --git a/doc/en/user/source/webadmin/tilecache/demopage.rst b/doc/en/user/source/webadmin/tilecache/demopage.rst new file mode 100644 index 00000000000..4fbcf9ad772 --- /dev/null +++ b/doc/en/user/source/webadmin/tilecache/demopage.rst @@ -0,0 +1,75 @@ +.. _webadmin_tilecaching_demo: + +Demo page +========= + +In addition to the :ref:`webadmin_tilecaching_layers` page, there is also a demo page where you can view configured layers, reload the configuration (when changing settings or adding new layers), and seed/refresh the existing cache on a per-layer basis. + +As this interface is part of the standalone GeoWebCache, some of the functionality here is duplicated from the :ref:`webadmin_tilecaching_layers` page. + +.. figure:: img/demopage.png + :align: center + + *Built-in demo page* + +Viewing +------- + +To view the demo page, append ``/gwc/demo`` to the address of your GeoServer instance. For example, if your GeoServer is at the following address:: + + http://localhost:8080/geoserver + +The GeoWebCache demo page is accessible here:: + + http://localhost:8080/geoserver/gwc/demo + +If there is a problem loading this page, verify the steps on the :ref:`gwc_using` page have been carried out successfully. + +Reload configuration +-------------------- + +The demo page contains a list of every layer that GeoWebCache is aware of. This is typically (though not necessarily) identical to the list of layers as published in the GeoServer WMS capabilities document. If configuration changes are made to GeoServer, GeoWebCache will not automatically become aware of them. To ensure that GeoWebCache is using the latest configuration information, click the **Reload Configuration** button. Reloading the configuration will trigger authentication to GeoServer, and will require an administration username and password. Use the same username and password that you would use to log in to the :ref:`web_admin`. (See :ref:`webadmin_basics` for more information.) After a successful login, the number of layers found and loaded will be displayed. + +.. figure:: img/demopage_reload.png + :align: center + + *Reloading the configuration* + +Layers and output formats +------------------------- + +For each layer that GeoWebCache serves, links are typically available for a number of different projections and output formats. By default, **OpenLayers** applications are available using image formats of PNG, PNG8, GIF, and JPEG in both **EPSG:4326** (standard lat/lon) and **EPSG:900913** (used in Google Maps) projections. In addition, **KML output** is available (EPSG:4326 only) using the same image formats, plus vector data ("kml"). + +Also on the list is an option to seed the layers (:guilabel:`Seed this layer`). More on this option below. + +Seeding +------- + +You can configure seeding processes via the :ref:`web_admin`. See the :ref:`webadmin_tilecaching_layers` page for more information. + +It is also possible to configure seeding process via the :ref:`webadmin_tilecaching_demo`. The page contains a link next to each layer entitled :guilabel:`Seed this layer`. This link will trigger authentication with the GeoServer configuration. Use the same username and password that you would use to log in to the :ref:`web_admin`. (See :ref:`webadmin_basics` for more information.) After a successful login, a new page shows up with seeding options. + +The seeding options page contains various parameters for configuring the way that the layer is seeded. + +.. list-table:: + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - ``Number of threads to use`` + - Possible values are between **1** and **16**. + * - ``Type of operation`` + - Sets the operation. There are three possible values: **Seed** (creates tiles, but does not overwrite existing ones), **Reseed** (like Seed, but overwrites existing tiles) and **Truncate** (deletes all tiles within the given parameters) + * - ``SRS`` + - Specifies the projection to use when creating tiles (default values are **EPSG:4326** and **EPSG:900913**) + * - ``Format`` + - Sets the image format of the tiles. Can be **application/vnd.google-earth.kml+xml** (Google Earth KML), **image/gif** (GIF), **image/jpeg** (JPEG), **image/png** (24 bit PNG), and **image/png8** (8 bit PNG) + * - ``Zoom start`` + - Sets the minimum zoom level. Lower values indicate map views that are more zoomed out. When seeding, GeoWebCache will only create tiles for those zoom levels inclusive of this value and ``Zoom stop``. + * - ``Zoom stop`` + - Sets the maximum zoom level. Higher values indicate map views that are more zoomed in. When seeding, GeoWebCache will only create tiles for those zoom levels inclusive of this value and ``Zoom start``. + * - ``Bounding box`` + - *(optional)* Allows seeding to occur over a specified extent, instead of the full extent of the layer. This is useful if your layer contains data over a large area, but the application will only request tiles from a subset of that area. The four boxes correspond to **Xmin**, **Ymin**, **Xmax**, and **Ymax**. + +.. warning:: Currently there is no progress bar to inform you of the time required to perform the operation, nor is there any intelligent handling of disk space. In short, the process may take a *very* long time, and the cache may fill up your disk. You may wish to set a :ref:`gwc_diskquota` before running a seed job. diff --git a/doc/en/user/source/webadmin/tilecache/gridsets.rst b/doc/en/user/source/webadmin/tilecache/gridsets.rst index b3203c628d4..1663e564fe0 100644 --- a/doc/en/user/source/webadmin/tilecache/gridsets.rst +++ b/doc/en/user/source/webadmin/tilecache/gridsets.rst @@ -5,7 +5,7 @@ Gridsets A gridset is a definition that specifies a spatial reference system, bounding box (extent), a list of zoom levels (resolutions or scale denominators), and tile dimensions. Tile requests must conform to the gridset matrix, otherwise caching will not occur. -This page allows you to edit existing saved gridsets or create new ones. There are only two coordinate reference systems (CRS) available in the default gridsets (EPSG:4326, EPSG:900913), so a new gridset will need to be created to support an additional CRS. Another reason to create a new gridset would be to set a different tile size, or different number of zoom levels. +This page allows you to edit existing saved gridsets or create new ones. There are five preconfuigred gridsets, all in one of two coordinate reference systems: EPSG:4326 and EPSG:900913. For additional CRS support, new gridsets can be created. Another reason to create a new gridset would be to set a different tile size or different number of zoom levels. .. figure:: img/gridsets.png :align: center diff --git a/doc/en/user/source/webadmin/tilecache/img/demopage.png b/doc/en/user/source/webadmin/tilecache/img/demopage.png new file mode 100644 index 0000000000000000000000000000000000000000..413b75a87700bbed32b4c711e8f3c2cf2095b97e GIT binary patch literal 18032 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 ZT!*WuzhSQ=-0c%?=9OVeWeVoL{|~@bstEu9 literal 0 HcmV?d00001 diff --git a/doc/en/user/source/webadmin/tilecache/index.rst b/doc/en/user/source/webadmin/tilecache/index.rst index 141a0d4b16a..2be6e1764ae 100644 --- a/doc/en/user/source/webadmin/tilecache/index.rst +++ b/doc/en/user/source/webadmin/tilecache/index.rst @@ -18,6 +18,7 @@ The pages in this menu can be accessed on the left side of the screen under the :maxdepth: 2 layers + demopage defaults gridsets - diskquotas \ No newline at end of file + diskquotas diff --git a/doc/en/user/source/webadmin/tilecache/layers.rst b/doc/en/user/source/webadmin/tilecache/layers.rst index ad69392003d..8458c9ad616 100644 --- a/doc/en/user/source/webadmin/tilecache/layers.rst +++ b/doc/en/user/source/webadmin/tilecache/layers.rst @@ -8,6 +8,8 @@ This page shows a listing of all of the layers known to the integrated GeoWebCac .. figure:: img/tilelayers.png :align: center +.. note:: There is also a link to the `GeoWebCache standalone demo page `. + Layer information ----------------- @@ -49,7 +51,7 @@ Will remove all saved tiles from the cache. Operation is identical to a full tr Add or remove cached layers --------------------------- -The list of layers displayed on this page is typically the same or similar as the full list of layers known to GeoServer. However, however it may not be desirable to have every layer published in GeoServer also have a cached layer component. In this case, simply check the box next to the layer to remove, and click on :guilabel:`Remove selected cached layers`. The layer will be removed from GeoWebCache, and the disk cache for this layer will be entirely removed. +The list of layers displayed on this page is typically the same or similar as the full list of layers known to GeoServer. However, it may not be desirable to have every layer published in GeoServer also have a cached layer component. In this case, simply check the box next to the layer to remove, and click on :guilabel:`Remove selected cached layers`. The layer will be removed from GeoWebCache, and the disk cache for this layer will be entirely removed. .. warning:: The deletion of the tile cache is not undoable. From cea95856bd472efeeba87cea52c607a739369590 Mon Sep 17 00:00:00 2001 From: jdeolive Date: Mon, 6 Aug 2012 21:10:55 -0600 Subject: [PATCH 19/72] updated community module doc, web/pom.xml -> web/app/pom.xml --- doc/en/developer/source/policies/community-modules.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/developer/source/policies/community-modules.rst b/doc/en/developer/source/policies/community-modules.rst index 8f1a3652182..bf109c5e4f8 100644 --- a/doc/en/developer/source/policies/community-modules.rst +++ b/doc/en/developer/source/policies/community-modules.rst @@ -119,7 +119,7 @@ The following outlines the steps to be taken in order to add a new community mod - #. Edit ``web/pom.xml`` and add the following inside of the ```` + #. Edit ``web/app/pom.xml`` and add the following inside of the ```` element: .. code-block:: xml @@ -250,7 +250,7 @@ Process myCommunityModule - #. Edit ``web/pom.xml`` and move the dependency on the community module + #. Edit ``web/app/pom.xml`` and move the dependency on the community module into the main dependencies section of the pom. Then remove the profile *Extensions* From 9c18b6575a1dc96a80bca800b2a0c7ada8d55ba2 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Tue, 7 Aug 2012 12:49:43 +0200 Subject: [PATCH 20/72] [GEOS-5209] Avoid altering attribute name case in GetFeatureInfo HTML template --- .../src/main/resources/org/geoserver/wms/featureinfo/header.ftl | 1 - .../resources/org/geoserver/wms/map/OpenLayersMapTemplate.ftl | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/wms/src/main/resources/org/geoserver/wms/featureinfo/header.ftl b/src/wms/src/main/resources/org/geoserver/wms/featureinfo/header.ftl index 76579903bb5..0ec71064c24 100644 --- a/src/wms/src/main/resources/org/geoserver/wms/featureinfo/header.ftl +++ b/src/wms/src/main/resources/org/geoserver/wms/featureinfo/header.ftl @@ -32,7 +32,6 @@ like td, tr, and so on. text-align:left; font-size:100%; font-weight:bold; - text-transform:uppercase; padding:.2em .2em; } diff --git a/src/wms/src/main/resources/org/geoserver/wms/map/OpenLayersMapTemplate.ftl b/src/wms/src/main/resources/org/geoserver/wms/map/OpenLayersMapTemplate.ftl index 9737e518363..fb16b9195dd 100644 --- a/src/wms/src/main/resources/org/geoserver/wms/map/OpenLayersMapTemplate.ftl +++ b/src/wms/src/main/resources/org/geoserver/wms/map/OpenLayersMapTemplate.ftl @@ -83,7 +83,6 @@ table.featureInfo th { padding: .2em .2em; - text-transform: uppercase; font-weight: bold; background: #eee; } @@ -100,7 +99,6 @@ text-align: left; font-size: 100%; font-weight: bold; - text-transform: uppercase; padding: .2em .2em; } From 082406e62e10e58454439c656c0950727d54af72 Mon Sep 17 00:00:00 2001 From: jdeolive Date: Mon, 6 Aug 2012 18:53:05 -0600 Subject: [PATCH 21/72] tweaked time boxed release schedule docs --- doc/en/developer/source/index.rst | 2 +- .../source/release-schedule/index.rst | 136 ++++++++++++++++++ .../timeboxed.png | Bin .../source/time-boxed-releases/index.rst | 105 -------------- 4 files changed, 137 insertions(+), 106 deletions(-) create mode 100644 doc/en/developer/source/release-schedule/index.rst rename doc/en/developer/source/{time-boxed-releases => release-schedule}/timeboxed.png (100%) delete mode 100644 doc/en/developer/source/time-boxed-releases/index.rst diff --git a/doc/en/developer/source/index.rst b/doc/en/developer/source/index.rst index 7f0f5758fe6..e3fbd9d9110 100644 --- a/doc/en/developer/source/index.rst +++ b/doc/en/developer/source/index.rst @@ -14,7 +14,7 @@ Welcome to the GeoServer Developer Manual. The manual is for those who want to eclipse-guide/index findbugs-guide/index programming-guide/index - time-boxed-releases/index + release-schedule/index release-guide/index release-testing-checklist/index cite-test-guide/index diff --git a/doc/en/developer/source/release-schedule/index.rst b/doc/en/developer/source/release-schedule/index.rst new file mode 100644 index 00000000000..38c2fae1d8c --- /dev/null +++ b/doc/en/developer/source/release-schedule/index.rst @@ -0,0 +1,136 @@ +.. _time_boxed_releases: + +Release Schedule +================ + +Starting with version 2.2 GeoServer releases follow a time boxed model in which releases occur +at regular predictable frequencies rather than at ad-hoc dates. In a time boxed the software is +released at predictable frequencies with whatever fixes, improvements, and feature are available +at the time of release. This differs from the previous feature based model in which releases occur +when a certain number of key features have accumulated. + +To compensate for the inherent unpredictability of the release contents the model includes strict +rules about what type of development is appropriate during specific periods of a branches life +cycle. These rules include a suitably long hardening period for the unstable branch to mature and +eventually become stable. + +Release branches +---------------- + +At any given time GeoServer is undergoing two to three main branches of development. + +#. The *stable* branch in which only bug fixing, and smaller scale feature development occurs on +#. The *unstable* (master) branch in which more experimental and larger scale feature development occurs +#. The *maintenance* branch which was the previously stable branch that is nearing end of life and sees + only the most stable development, typically mostly bug fixes. + +Release cycle +------------- + +On the stable branches release follow a monthly cycle in a which a new minor release occurs once a +month. On the unstable branch releases follow a 6 month cycle in which a new major release occurs +once every 6 months. The following diagram illustrates the cycle: + +.. image:: timeboxed.png + +Things to note: + +* monthly releases on the stable branch +* a four month open development period followed by two months hardening period on the unstable branch +* beta releases are supposed to be released out of the unstable series on a monthly basis + across the switch between open development and hardening, followed by the first release candidate +* release candidates are pushed out every two weeks until we reach a stable code base, which will be released + as the new major stable release +* the first release candidate marks the branch off of a new trunk, on which open development starts again + +The time of the first release candidate marks the origin of a new stable branch in which the unstable branch +becomes the stable branch, and the stable branches becomes a maintenance branch. + +Every month, on the same day, a new release is issued from the stable branch using whatever revision of +GeoServer/Geotools passed the last CITE tests. The release is meant to improve upon the previous release in +both functionality and stability, so unless the project steering committee determines reasons to block the release +it will happen regardless of what bug reports are in Jira. Pending resourcing, they can be fixed in the next release +that comes out one month later. + +At any given point in time there are two branches under the development, the stable branch and the master/unstable +branch. Once every six months when the creation of a new stable branch occurs a third active maintenance branch +is created. This branch is kept up to date with the stable stable for a period of one-month after which the final +release on that branch is created. That said there is nothing against a developer continuing to maintain the branch +or creating release from it, it is just not expected. + +Development phases +------------------ + +The type of development that can occur on a branch is dictated by where the branch is in terms of its life cycle. + +Stable branch +````````````` + +The type of acceptable development on the stable branch does not change much over its lifetime. It is meant +for bug fixes and new features that do not affect the GeoServer API or significantly affect the stability. +A PSC vote (with eventual proposal) can be called in case a significant new feature or change needs +to be back ported to the stable branch overriding the above rules. + +If, for any reason, a release is delayed the next release will be rescheduled 30 days after the last release +(that is, delaying the whole train of remaining releases). + +Unstable branch +``````````````` + +The type of development on the master/unstable branch changes over its lifetime from four months of open +development to two months of stable/hardening development. + +Open development phase +`````````````````````` + +The open development phase starts when the new stable release is branched off, and ends when hardening +starts, four months after the new stable release is made. + +During this phase developers are free to commit stability undermining changes (even significant ones). +Those changes still need to be voted as GSIP anyways to ensure, as usual, resourcing checks, API consistency +and community review. + +After three months from the release of the stable series a first beta will be released, +one month after that the second beta will be released and the branch will switch into hardening mode. + +Hardening phase +``````````````` + +The hardening phase starts when the second beta is released and continues through all release candidate (RC) +releases. The first RC is released one month after the second beta, and then bi-weekly releases +will be issued until no major issues will be reported by the user base, at which point the last RC +will be turned into the new stable release. + +During hardening only bug-fixes and new non core plugins can be developed + +Commit rules +------------ + +The following are guidelines as to what types of commits can occur in any particular phase. While the PSC +reserves the right to vote and override the committing guidelines the following rules reflect the +current policies. + +The **hardening phase**, and by extension the stable and open phases, can receive any of the following +types of commits: + +* bug fixes +* documentation improvements +* new plugins contributed as community or extension modules (with a cautionary note that during + hardening the attention should be concentrated as much as possible on getting the new release stable) + +In addition the **stable phase** can receive any of the following types of commits: + +* new minor core functionality (e.g. new output format) +* new API that we can commit to for a long period of time (provided it's not a change to existing API unless the PSC votes otherwise). + GeoServer is not a library, so defining API can be hard, but any class that can be used by pluggable + extension points should be changed with care, especially so in a stable series + +In addition to the above the **stable phase** can receive the following types of changes provided there are no +reservations or concerns about them from the PSC. Such changes may be subject to voting: + +* promotion of extensions to core +* core changes that are unlikely to affect the stability of the upcoming release + (if the PSC is ok better land them right after a release to get as a large window for testing as possible) +* back port of larger changes that have proven to be working well on trunk for an extended period of time + +During the **open development phase** all types of commits are fair game but of course large changes are still subject to proposals and reviews. \ No newline at end of file diff --git a/doc/en/developer/source/time-boxed-releases/timeboxed.png b/doc/en/developer/source/release-schedule/timeboxed.png similarity index 100% rename from doc/en/developer/source/time-boxed-releases/timeboxed.png rename to doc/en/developer/source/release-schedule/timeboxed.png diff --git a/doc/en/developer/source/time-boxed-releases/index.rst b/doc/en/developer/source/time-boxed-releases/index.rst deleted file mode 100644 index 98ef6d34c5d..00000000000 --- a/doc/en/developer/source/time-boxed-releases/index.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. _time_boxed_releases: - -Time boxed releases -=================== - -The release model, starting with GeoServer 2.2.0, is based on time boxing, that is, a setup in which -the software is released: -* at predictable dates with whatever -* with whatever fix/improvements are available at the time - -To compensate the eventual unpredictability of the release contents the model includes strict rules -about what might be committed on each branch, and a suitably long hardening period in which the -unstable series gets stabilized. - -Release timings ---------------- - -The cycle is based on: -* monthly releases on the stable series -* a four month open development period followed by two months hardening period -* beta releases are supposed to be released out of the unstable series on a monthly basis - across the switch between open development and hardening, followed by the first RC -* RC are pushed out every two weeks until we reach a stable code base, which will be released - as the new major stable release -* the first RC marks the branch off of a new trunk, on which open development starts again - -The following picture exemplifies the cycle: - - .. image:: timeboxed.png - -Every month, on the same day, a new release is issued using whatever revision of GeoServer/Geotools passed the last CITE tests. -The release is meant to improve upon the previous release in both functionality and stability, so unless -the project steering committee determines reasons to block the release it will happen regardless of what bug -reports are in Jira (pending resourcing, they can be fixed in the next release that comes out one month later). - -At every point in time there are two branches, a stable branch and a trunk, with just one month every -six where there are three active branches (nothing prevents developers willing to keep the stable series -up longer working there if they wish to, it's just not expected anymore). - -The three phases ----------------- - -Stable branch -````````````` - -The stable branch is meant for bug fixes and new features that do not affect the GeoServer API or -significantly affect the stability. -A PSC vote (with eventual proposal) can be called in case a significant new feature or change needs -to be back ported to the stable branch overriding the above rules. - -If, for any reason, a release is delayed the next release will be rescheduled 30 days after the last release -(that is, delaying the whole training of remaining releases). - -Trunk in open development mode -`````````````````````````````` - -The open development mode starts when the new stable release is branched off, and ends when hardening -starts, four months after the new stable release is made. - -During this operational mode developers are free to commit stability undermining changes (even significant ones). -Those changes still need to be voted as GSIP anyways to ensure, as usual, resourcing checks, API consistency and community review. - -After three months from the release of the stable series a first beta will be released, -one month after that the second beta will be released and the trunk will switch into hardening mode. - -Trunk in hardening mode -``````````````````````` - -The harderning mode starts when the second beta is released and continuous through all release candidate -releases. The first RC is released one month after the second beta, and then bi-weekly releases -will be issued until no major issues will be reported by the user base, at which point the last RC -will be turned into the new stable release. - -During harderning only bug-fixes and new non core plugins can be developed - -Commit rules ------------- - -While the PSC is going to be able to vote and override the committing guidelines, it is still good -to have some reference and default of what can be done or not done in the various branches. - -**Hardening mode**, and by extension, stable and trunk, can take any of the following: - -* bug fixes -* documentation improvements -* new plugins contributed as community or extension modules (with a cautionary note that during - hardening the attention should be concentrated as much as possible on getting the new release stable) - - -**Stable branch** can take in addition the following: - -* new minor core functionality (e.g. new output format, -* new API that we can commit to for a long period of time (provided it's not a change to existing API unless the PSC votes otherwise). - GeoServer is not a library, so defining API can be hard, but any class that can be used by pluggable - extension points should be changed with care, especially so in a stable series - -In addition to the above the **stable branch** can get the following changes provided there is -a **solid PSC/PMC vote** (e.g., no doubts or concerns about them) to include them: - -* promotion of extensions to core -* core changes that are unlikely to affect the stability of the upcoming release - (if the PSC is ok better land them right after a release to get as a large window for testing as possible) -* backport of larger changes that have proven to be working well on trunk for an extended period of time - -**Trunk** in open development mode can take everything, of course large changes are still subject to proposals and reviews. \ No newline at end of file From f821409157530e53991372270232dbdb1ba316e3 Mon Sep 17 00:00:00 2001 From: Michael Romero Date: Mon, 6 Aug 2012 16:44:28 -0700 Subject: [PATCH 22/72] Added security module to src assembly and fixed path to minimal data_dir. Backport of pull request #14. --- src/release/src.xml | 5 +++-- src/wms/src/main/java/org/geoserver/wms/GetCapabilities.java | 2 +- .../geoserver/wms/legendgraphic/RasterLayerLegendHelper.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/release/src.xml b/src/release/src.xml index 7faae80d7e5..6c1736334ff 100644 --- a/src/release/src.xml +++ b/src/release/src.xml @@ -10,11 +10,12 @@ org.geoserver:platform org.geoserver:ows org.geoserver:main + org.geoserver:security org.geoserver:wfs org.geoserver:wms org.geoserver:web org.geoserver:wcs - org.geoserver:wcs1_0 + org.geoserver:wcs1_0 org.geoserver:wcs1_1 org.geoserver:gwc org.geoserver:rest @@ -64,7 +65,7 @@ - web/app/minimal + ../data/minimal data/minimal **/* diff --git a/src/wms/src/main/java/org/geoserver/wms/GetCapabilities.java b/src/wms/src/main/java/org/geoserver/wms/GetCapabilities.java index 1ab46a5a8cd..f97ce1e3691 100644 --- a/src/wms/src/main/java/org/geoserver/wms/GetCapabilities.java +++ b/src/wms/src/main/java/org/geoserver/wms/GetCapabilities.java @@ -55,7 +55,7 @@ public TransformerBase run(final GetCapabilitiesRequest request) throws ServiceE long geoUS = wms.getUpdateSequence(); if (reqUS > geoUS) { throw new ServiceException( - "Client supplied an updateSequence that is greater than the current sever updateSequence", + "Client supplied an updateSequence that is greater than the current server updateSequence", "InvalidUpdateSequence"); } if (reqUS == geoUS) { diff --git a/src/wms/src/main/java/org/geoserver/wms/legendgraphic/RasterLayerLegendHelper.java b/src/wms/src/main/java/org/geoserver/wms/legendgraphic/RasterLayerLegendHelper.java index b1ebbd1558b..34f21c20cb1 100644 --- a/src/wms/src/main/java/org/geoserver/wms/legendgraphic/RasterLayerLegendHelper.java +++ b/src/wms/src/main/java/org/geoserver/wms/legendgraphic/RasterLayerLegendHelper.java @@ -123,7 +123,7 @@ private void parseRequest(final GetLegendGraphicRequest request) { height = request.getHeight(); if (width <= 0 || height <= 0) throw new IllegalArgumentException( - "Invalid widht and or height for the GetLegendGraphicRequest"); + "Invalid width and or height for the GetLegendGraphicRequest"); final Symbolizer[] symbolizers = applicableRules[0].getSymbolizers(); if (symbolizers == null || symbolizers.length != 1 | symbolizers[0] == null) From 0ff5916369a9f9b4a2f75a11ee31c8d655dbb5fe Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Tue, 7 Aug 2012 12:27:30 -0700 Subject: [PATCH 23/72] Added GWC If-Modified-Since info, plus typo fixes --- doc/en/user/source/geowebcache/config.rst | 7 +- .../source/geowebcache/responseheaders.rst | 95 +++++++++++++++++-- doc/en/user/source/geowebcache/using.rst | 3 + 3 files changed, 94 insertions(+), 11 deletions(-) diff --git a/doc/en/user/source/geowebcache/config.rst b/doc/en/user/source/geowebcache/config.rst index f7f4c973bc2..6557e2e94ff 100644 --- a/doc/en/user/source/geowebcache/config.rst +++ b/doc/en/user/source/geowebcache/config.rst @@ -17,17 +17,16 @@ GeoWebCache has a full integrated web-based configuration. See the :ref:`webadm Determining tiled layers ------------------------ -In versions of GeoServer priori to 2.2.0, the GeoWebCache integration was done in a such way that every GeoServer layer and layer group was forced to have an associated GeoWebCache tile layer. In addition, every such tile layer was forcedly published in the EPSG:900913 and EPSG:4326 gridsets with PNG and JPEG output formats. +In versions of GeoServer prior to 2.2.0, the GeoWebCache integration was done in a such way that every GeoServer layer and layer group was forced to have an associated GeoWebCache tile layer. In addition, every such tile layer was forcedly published in the EPSG:900913 and EPSG:4326 gridsets with PNG and JPEG output formats. Now, it is possible to selectively turn caching on or off for any layer served through GeoServer. This setting can be done on the :ref:`webadmin_tilecaching_layers` section in the :ref:`web_admin`. Configuration files ------------------- +It is possible to configure most aspects of cached layers through the :ref:`webadmin_tilecaching` section in the :ref:`web_admin` or the :ref:`gwc_rest`. -It is possible to configure most aspects of cached layers through the :ref:`webadmin_tilecaching` section in the :ref:`web_admin`. - -GeoWebCache keeps the configuration for each GeoServer tiled layer separately, inside the :file:`/gwc-layers/` directory. There is one XML file for each tile layer. These files contain a different syntax from the ```` syntax in the standalone version and are *not* meant to be edited by hand. Instead you can configure tile layers on the :ref:`webadmin_tilecaching_layers` page. +GeoWebCache keeps the configuration for each GeoServer tiled layer separately, inside the :file:`/gwc-layers/` directory. There is one XML file for each tile layer. These files contain a different syntax from the ```` syntax in the standalone version and are *not* meant to be edited by hand. Instead you can configure tile layers on the :ref:`webadmin_tilecaching_layers` page or through the :ref:`gwc_rest`. Configuration for the defined gridsets still is saved in :file:`/gwc/geowebcache.xml`` so that the integrated GeoWebCache can continue to serve externally-defined tile layers from WMS services outside GeoServer. diff --git a/doc/en/user/source/geowebcache/responseheaders.rst b/doc/en/user/source/geowebcache/responseheaders.rst index 30e34f05a34..685657c18ac 100644 --- a/doc/en/user/source/geowebcache/responseheaders.rst +++ b/doc/en/user/source/geowebcache/responseheaders.rst @@ -3,17 +3,25 @@ HTTP Response Headers ===================== -GeoWebCache returns both standard and custom HTTP response headers when serving a tile request, both to adhere to an HTTP 1.1 transfer control mechanism and to aid in debugging problems. +The GeoWebCache integrated with GeoServer employs special information stored in the header of responses. These headers are only available with direct calls to the :ref:`GeoWebCache endpoint `, and not with :ref:`direct WMS integration `. + +Custom response headers +----------------------- + +GeoWebCache returns both standard and custom HTTP response headers when serving a tile request. This aids in the debugging process, as well as adhering to an HTTP 1.1 transfer control mechanism. The response headers can be determined via a utility such as `cURL `_. Example -------- +~~~~~~~ +.. note:: For all cURL commands below, make sure to replace ``>/dev/null`` with ``>nul`` if you are running on Windows. -This is a sample request and response using cURL:: +This is a sample request and response using cURL: + +.. code-block:: console - curl -v "http://localhost:8080/geowebcache/service/wms?LAYERS=sde%3Abmworld&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=-180,-38,-52,90&WIDTH=256&HEIGHT=256&tiled=true" > /dev/null + curl -v "http://localhost:8080/geoserver/gwc/service/wms?LAYERS=sde%3Abmworld&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=-180,-38,-52,90&WIDTH=256&HEIGHT=256&tiled=true" > /dev/null :: @@ -31,10 +39,10 @@ This is a sample request and response using cURL:: From this, one can learn that the tile was found in the cache (``HIT``), the requested tile was from the gridset called ``GlobalCRS84Pixel`` and had a CRS of ``EPSG:4326``. -Full list of response headers ------------------------------ +List of custom response headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following is the full list of response headers. Whenever GeoWebCache serves a tile request, it will write some or all of the following custom headers on the HTTP response. +The following is the full list of custom response headers. Whenever GeoWebCache serves a tile request, it will write some or all of the following custom headers on the HTTP response. .. list-table:: :header-rows: 1 @@ -56,3 +64,76 @@ The following is the full list of response headers. Whenever GeoWebCache serves - Name of the gridset the tile belongs to (see :ref:`webadmin_tilecaching_gridsets` for more information) * - ``geowebcache-crs`` - Coordinate reference system code of the matching gridset (e.g. ``EPSG:900913``, ``EPSG:4326``, etc). + +.. _gwc_lastmodifiedheaders: + +Last-Modified and If-Modified-Since +----------------------------------- + +Well behaved HTTP 1.1 clients and server applications can make use of ``Last-Modified`` and ``If-Modified-Since`` HTTP control mechanisms to know when locally cached content is up to date, eliminating the need to download the same content again. This can result in considerable bandwidth savings. (See HTTP 1.1 `RFC 2616 `_, sections 14.29 and 14.25, for more information on these mechanisms.) + +GeoWebCache will write a ``Last-Modified`` HTTP response header when serving a tile image. The date is written as an RFC-1123 ``HTTP-Date``:: + + Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT + +Clients connecting to GeoWebCache can create a "conditional GET" request with the ``If-Modified-Since`` request header. If the tile wasn't modified after the date specified in the ``Last-Modified`` response header, GeoWebCache will return a ``304`` status code indicating that the resource was available and not modified. + +Example +~~~~~~~ + +A query for a specific tile returns the ``Last-Modified`` response header: + +.. code-block:: console + + curl -v "http://localhost:8080/geoserver/gwc/service/wms?LAYERS=img%20states&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326&BBOX=-135,45,-90,90&WIDTH=256&HEIGHT=256" >/dev/null + +:: + + > Host: localhost:8080 + > Accept: */* + > + < HTTP/1.1 200 OK + ... + < Last-Modified: Wed, 25 Jul 2012 00:42:00 GMT + < Content-Type: image/png + < Content-Length: 31192 + +This request has the ``If-Modified-Since`` header set to one second after what was returned by ``Last-Modified``: + +.. code-block:: console + + curl --header "If-Modified-Since: Wed, 25 Jul 2012 00:42:01 GMT" -v "http://localhost:8080/geoserver/gwc/service/wms?LAYERS=img%20states&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326&BBOX=-135,45,-90,90&WIDTH=256&HEIGHT=256" >/dev/null + +:: + + > Host: localhost:8080 + > Accept: */* + > If-Modified-Since: Wed, 25 Jul 2012 00:42:01 GMT + > + < HTTP/1.1 304 Not Modified + < Last-Modified: Wed, 25 Jul 2012 00:42:00 GMT + < Content-Type: image/png + < Content-Length: 31192 + +The response code is ``304``. As the file hasn't been modified since the time specified in the request, no content is actually transferred. The client is informed that its copy of the tile is up to date. + +However, if you were to set the ``If-Modified-Since`` header to *before* the time stored in ``Last-Modified``, you will instead receive a ``200`` status code and the tile will be downloaded. + +This example sets the ``If-Modified-Since`` header to one second before what was returned by ``Last-Modified``: + +.. code-block:: console + + curl --header "If-Modified-Since: Wed, 25 Jul 2012 00:41:59 GMT" -v "http://localhost:8080/geoserver/gwc/service/wms?LAYERS=img%20states&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326&BBOX=-135,45,-90,90&WIDTH=256&HEIGHT=256" >/dev/null + +:: + + > Host: localhost:8080 + > Accept: */* + > If-Modified-Since: Wed, 25 Jul 2012 00:41:59 GMT + > + < HTTP/1.1 200 OK + ... + < Last-Modified: Wed, 25 Jul 2012 00:42:00 GMT + < Content-Type: image/png + < Content-Length: 31192 + diff --git a/doc/en/user/source/geowebcache/using.rst b/doc/en/user/source/geowebcache/using.rst index db6c872912d..6b725c0aeab 100644 --- a/doc/en/user/source/geowebcache/using.rst +++ b/doc/en/user/source/geowebcache/using.rst @@ -5,6 +5,7 @@ Using GeoWebCache .. note:: For an more in-depth discussion of using GeoWebCache, please see the `GeoWebCache documentation `_. +.. _gwc_directwms: Direct integration with GeoServer WMS ------------------------------------- @@ -62,6 +63,8 @@ With direct WMS integration, the following parameter filters are supported for G If a request is made using any of the above parameters, the request will be passed to GeoServer, unless a parameter filter has been set up, in which case GeoWebCache will process the request. +.. _gwc_endpoint: + GeoWebCache endpoint URL ------------------------ From c74e7b99325e0f603a4132b27ecb22f2734b4e72 Mon Sep 17 00:00:00 2001 From: Mike Pumphrey Date: Tue, 7 Aug 2012 16:43:51 -0700 Subject: [PATCH 24/72] GWC response headers actually work with direct WMS integration --- doc/en/user/source/geowebcache/responseheaders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/user/source/geowebcache/responseheaders.rst b/doc/en/user/source/geowebcache/responseheaders.rst index 685657c18ac..d0262523319 100644 --- a/doc/en/user/source/geowebcache/responseheaders.rst +++ b/doc/en/user/source/geowebcache/responseheaders.rst @@ -3,7 +3,7 @@ HTTP Response Headers ===================== -The GeoWebCache integrated with GeoServer employs special information stored in the header of responses. These headers are only available with direct calls to the :ref:`GeoWebCache endpoint `, and not with :ref:`direct WMS integration `. +The GeoWebCache integrated with GeoServer employs special information stored in the header of responses. These headers are available either with direct calls to the :ref:`GeoWebCache endpoint ` or with :ref:`direct WMS integration `. Custom response headers ----------------------- From 93d632ca31658889879418b165f1ac96423d6dd5 Mon Sep 17 00:00:00 2001 From: Daniele Romagnoli Date: Wed, 8 Aug 2012 16:29:49 +0200 Subject: [PATCH 25/72] -GEOS-5226: CoverageAccessSettings and JAI page and global change events handling fix --- .../geoserver/config/CoverageAccessInfo.java | 5 +- .../java/org/geoserver/config/JAIInfo.java | 8 ++-- .../config/impl/CoverageAccessInfoImpl.java | 8 +++- .../geoserver/config/impl/JAIInfoImpl.java | 8 ++++ .../coverage/CoverageAccessInitializer.java | 8 +++- .../org/geoserver/jai/JAIInitializer.java | 8 +++- .../web/admin/CoverageAccessPage.java | 11 +++-- .../java/org/geoserver/web/admin/JAIPage.java | 11 +++-- .../geoserver/web/admin/ServerAdminPage.java | 48 +++++++++++++------ 9 files changed, 84 insertions(+), 31 deletions(-) diff --git a/src/main/src/main/java/org/geoserver/config/CoverageAccessInfo.java b/src/main/src/main/java/org/geoserver/config/CoverageAccessInfo.java index d7482c34410..d0c395dcdcc 100644 --- a/src/main/src/main/java/org/geoserver/config/CoverageAccessInfo.java +++ b/src/main/src/main/java/org/geoserver/config/CoverageAccessInfo.java @@ -4,6 +4,7 @@ */ package org.geoserver.config; +import java.io.Serializable; import java.util.concurrent.ThreadPoolExecutor; @@ -13,7 +14,7 @@ * @author Daniele Romagnoli, GeoSolutions * */ -public interface CoverageAccessInfo { +public interface CoverageAccessInfo extends Cloneable, Serializable{ public enum QueueType { UNBOUNDED, DIRECT @@ -60,5 +61,5 @@ public enum QueueType { void setImageIOCacheThreshold(long threshold); long getImageIOCacheThreshold(); - + public CoverageAccessInfo clone(); } diff --git a/src/main/src/main/java/org/geoserver/config/JAIInfo.java b/src/main/src/main/java/org/geoserver/config/JAIInfo.java index 8101701b513..18c256ce34c 100644 --- a/src/main/src/main/java/org/geoserver/config/JAIInfo.java +++ b/src/main/src/main/java/org/geoserver/config/JAIInfo.java @@ -4,18 +4,18 @@ */ package org.geoserver.config; +import java.io.Serializable; + import javax.media.jai.JAI; import javax.media.jai.TileCache; -import com.sun.media.jai.util.SunTileCache; - /** * Java Advanced Imaging configuration. * * @author Justin Deoliveira, OpenGeo * */ -public interface JAIInfo { +public interface JAIInfo extends Cloneable, Serializable{ /** * Flag controlling image interpolation. @@ -92,4 +92,6 @@ public interface JAIInfo { */ TileCache getTileCache(); void setTileCache(TileCache tileCache); + + public JAIInfo clone(); } diff --git a/src/main/src/main/java/org/geoserver/config/impl/CoverageAccessInfoImpl.java b/src/main/src/main/java/org/geoserver/config/impl/CoverageAccessInfoImpl.java index 5556b101572..7d278842b93 100644 --- a/src/main/src/main/java/org/geoserver/config/impl/CoverageAccessInfoImpl.java +++ b/src/main/src/main/java/org/geoserver/config/impl/CoverageAccessInfoImpl.java @@ -122,5 +122,11 @@ public boolean equals(Object obj) { return true; } - + public CoverageAccessInfoImpl clone() { + try { + return (CoverageAccessInfoImpl) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java b/src/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java index 3a336feb931..0f44afb8d5d 100644 --- a/src/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java +++ b/src/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java @@ -236,4 +236,12 @@ public boolean equals(Object obj) { return false; return true; } + + public JAIInfoImpl clone() { + try { + return (JAIInfoImpl) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/src/main/java/org/geoserver/coverage/CoverageAccessInitializer.java b/src/main/src/main/java/org/geoserver/coverage/CoverageAccessInitializer.java index 0294067648a..b42770be0f1 100644 --- a/src/main/src/main/java/org/geoserver/coverage/CoverageAccessInitializer.java +++ b/src/main/src/main/java/org/geoserver/coverage/CoverageAccessInitializer.java @@ -48,12 +48,16 @@ public void initialize(GeoServer geoServer) throws Exception { public void handleGlobalChange(GeoServerInfo global, List propertyNames, List oldValues, List newValues) { - initCoverage(global.getCoverageAccess()); + if (propertyNames.contains("coverageAccess")) { + // Make sure to proceed with coverageAccess init + // only in case the global change involved that section + initCoverage(global.getCoverageAccess()); + } } @Override public void handlePostGlobalChange(GeoServerInfo global) { - initCoverage(global.getCoverageAccess()); + // No need to handle that change too } }); } diff --git a/src/main/src/main/java/org/geoserver/jai/JAIInitializer.java b/src/main/src/main/java/org/geoserver/jai/JAIInitializer.java index b6b840e0128..7064fed23b5 100644 --- a/src/main/src/main/java/org/geoserver/jai/JAIInitializer.java +++ b/src/main/src/main/java/org/geoserver/jai/JAIInitializer.java @@ -33,12 +33,16 @@ public void handleGlobalChange(GeoServerInfo global, List propertyNames, List oldValues, List newValues) { - initJAI( global.getJAI() ); + if (propertyNames.contains("jAI")) {//TODO: check why the propertyname is reported as jAI instead of JAI + // Make sure to proceed with JAI init + // only in case the global change involved that section + initJAI(global.getJAI() ); + } } @Override public void handlePostGlobalChange(GeoServerInfo global) { - initJAI(global.getJAI()); + // No need to handle that change too } }); } diff --git a/src/web/core/src/main/java/org/geoserver/web/admin/CoverageAccessPage.java b/src/web/core/src/main/java/org/geoserver/web/admin/CoverageAccessPage.java index cf62234cd88..d3dc0a90482 100644 --- a/src/web/core/src/main/java/org/geoserver/web/admin/CoverageAccessPage.java +++ b/src/web/core/src/main/java/org/geoserver/web/admin/CoverageAccessPage.java @@ -21,7 +21,7 @@ import org.geoserver.config.CoverageAccessInfo; import org.geoserver.config.CoverageAccessInfo.QueueType; import org.geoserver.config.GeoServer; -import org.geoserver.web.GeoServerHomePage; +import org.geoserver.config.GeoServerInfo; import org.geoserver.web.wicket.ParamResourceModel; /** @@ -85,6 +85,10 @@ public void validate(Form form) { public CoverageAccessPage(){ final IModel geoServerModel = getGeoServerModel(); + + // this invokation will trigger a clone of the CoverageAccessInfo, + // which will allow the modification proxy seeing changes on the + // CoverageAccess page with respect to the original CoverageAccessInfo object final IModel coverageModel = getCoverageAccessModel(); // form and submit @@ -115,8 +119,9 @@ public CoverageAccessPage(){ @Override public void onSubmit() { GeoServer gs = (GeoServer) geoServerModel.getObject(); - gs.getGlobal().setCoverageAccess( (CoverageAccessInfo)coverageModel.getObject() ); - gs.save( gs.getGlobal() ); + GeoServerInfo global = gs.getGlobal(); + global.setCoverageAccess( (CoverageAccessInfo)coverageModel.getObject() ); + gs.save(global); doReturn(); } }; diff --git a/src/web/core/src/main/java/org/geoserver/web/admin/JAIPage.java b/src/web/core/src/main/java/org/geoserver/web/admin/JAIPage.java index 7695f6a1e9a..5e1585d9d2c 100644 --- a/src/web/core/src/main/java/org/geoserver/web/admin/JAIPage.java +++ b/src/web/core/src/main/java/org/geoserver/web/admin/JAIPage.java @@ -13,8 +13,8 @@ import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.validation.validator.NumberValidator; import org.geoserver.config.GeoServer; +import org.geoserver.config.GeoServerInfo; import org.geoserver.config.JAIInfo; -import org.geoserver.web.GeoServerHomePage; /** * Edits the JAI configuration parameters @@ -24,6 +24,10 @@ public class JAIPage extends ServerAdminPage { public JAIPage(){ final IModel geoServerModel = getGeoServerModel(); + + // this invokation will trigger a clone of the JAIInfo + // which will allow the modification proxy seeing changes on the + // Jai page with respect to the original JAIInfo object final IModel jaiModel = getJAIModel(); // form and submit @@ -54,8 +58,9 @@ public JAIPage(){ @Override public void onSubmit() { GeoServer gs = (GeoServer) geoServerModel.getObject(); - gs.getGlobal().setJAI( (JAIInfo)jaiModel.getObject() ); - gs.save( gs.getGlobal() ); + GeoServerInfo global = gs.getGlobal(); + global.setJAI( (JAIInfo)jaiModel.getObject()); + gs.save( global ); doReturn(); } }; diff --git a/src/web/core/src/main/java/org/geoserver/web/admin/ServerAdminPage.java b/src/web/core/src/main/java/org/geoserver/web/admin/ServerAdminPage.java index 988b7287712..3fd9ad70bb3 100644 --- a/src/web/core/src/main/java/org/geoserver/web/admin/ServerAdminPage.java +++ b/src/web/core/src/main/java/org/geoserver/web/admin/ServerAdminPage.java @@ -9,7 +9,10 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; import org.geoserver.catalog.DataStoreInfo; +import org.geoserver.config.CoverageAccessInfo; +import org.geoserver.config.JAIInfo; import org.geoserver.web.GeoServerSecuredPage; import org.geotools.data.DataAccess; import org.geotools.data.DataStore; @@ -38,22 +41,37 @@ public Object load() { }; } - public IModel getJAIModel(){ - return new LoadableDetachableModel(){ - public Object load() { - return getGeoServerApplication() - .getGeoServer().getGlobal().getJAI(); - } - }; - } - public IModel getCoverageAccessModel(){ - return new LoadableDetachableModel(){ - public Object load() { - return getGeoServerApplication() - .getGeoServer().getGlobal().getCoverageAccess(); - } - }; + public IModel getJAIModel(){ + // Notes setup on top of an explanation provided by Gabriel Roldan for + // his patch which fixes the modificationProxy unable to detect changes + // -------------------------------------------------------------------- + // with this change, we will edit a clone of the original JAIInfo. + // By this way, the modification proxy will count it as a change. + // The previous code wasn't working as expected. + // the reason is that the model used to edit JAIInfo is a + // LoadableDetachableModel, so when the edit page does gobal.setJAI, it + // is actually setting the same object reference, and hence the + // modificationproxy does not count it as a change. + + JAIInfo currJaiInfo = getGeoServerApplication().getGeoServer().getGlobal().getJAI().clone(); + return new Model(currJaiInfo); + } + + public IModel getCoverageAccessModel(){ + // Notes setup on top of an explanation provided by Gabriel Roldan for + // his patch which fixes the modificationProxy unable to detect changes + // -------------------------------------------------------------------- + // with this change, we will edit a clone of the original Info. + // By this way, the modification proxy will count it as a change. + // The previous code wasn't working as expected. + // the reason is that the model used to edit the page is a + // LoadableDetachableModel, so when the edit page does gobal.setJAI, it + // is actually setting the same object reference, and hence the + // modificationProxy does not count it as a change. + + CoverageAccessInfo currCoverageAccessInfo = getGeoServerApplication().getGeoServer().getGlobal().getCoverageAccess().clone(); + return new Model(currCoverageAccessInfo); } public IModel getContactInfoModel(){ From ff80be9f82c5363ccb36fe76b221035d5acbdd19 Mon Sep 17 00:00:00 2001 From: jdeolive Date: Thu, 9 Aug 2012 10:14:41 -0600 Subject: [PATCH 26/72] GEOS-5016, fixing test case based on assumptions of workspace != namespace --- .../DescribeLayerTransformerTest.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/wms/src/test/java/org/geoserver/wms/describelayer/DescribeLayerTransformerTest.java b/src/wms/src/test/java/org/geoserver/wms/describelayer/DescribeLayerTransformerTest.java index d565bc3e8c4..da62c4a9bf8 100644 --- a/src/wms/src/test/java/org/geoserver/wms/describelayer/DescribeLayerTransformerTest.java +++ b/src/wms/src/test/java/org/geoserver/wms/describelayer/DescribeLayerTransformerTest.java @@ -40,10 +40,6 @@ */ public class DescribeLayerTransformerTest extends TestCase { - private static final String TEST_NS_PREFIX = "geos"; - - private static final String TEST_NAMESPACE = "http//www.geoserver.org/test"; - /** * A request for the tests to fill up with the test spficic parameters. setUp creates it whit a * mocked up catalog @@ -77,9 +73,10 @@ protected void setUp() throws Exception { GeoServerImpl geoServerImpl = new GeoServerImpl(); catalog = new CatalogImpl(); geoServerImpl.setCatalog(catalog); + NamespaceInfoImpl ns = new NamespaceInfoImpl(); - ns.setPrefix(TEST_NS_PREFIX); - ns.setURI(TEST_NAMESPACE); + ns.setPrefix("fakeWs"); + ns.setURI("http://fakews.org"); WorkspaceInfoImpl workspace = new WorkspaceInfoImpl(); workspace.setId("fakeWs"); @@ -100,8 +97,8 @@ protected void setUp() throws Exception { vectorLayerInfo.setId("states"); vectorLayerInfo.setName("states"); - catalog.add(ns); catalog.add(workspace); + catalog.add(ns); catalog.add(dataStoreInfo); catalog.add(featureTypeInfo); catalog.add(vectorLayerInfo); @@ -197,7 +194,7 @@ public void testSingleVectorLayer() throws Exception { final String layerDescPath = "/WMS_DescribeLayerResponse/LayerDescription"; assertXpathExists(layerDescPath, dom); - assertXpathEvaluatesTo("geos:states", layerDescPath + "/@name", dom); + assertXpathEvaluatesTo("fakeWs:states", layerDescPath + "/@name", dom); final String expectedWfsAtt = serverBaseUrl + "/wfs/WfsDispatcher?"; assertXpathExists(layerDescPath + "/@wfs", dom); @@ -210,7 +207,7 @@ public void testSingleVectorLayer() throws Exception { assertXpathEvaluatesTo("WFS", layerDescPath + "/@owsType", dom); assertXpathExists(layerDescPath + "/Query", dom); - assertXpathEvaluatesTo("geos:states", layerDescPath + "/Query/@typeName", dom); + assertXpathEvaluatesTo("fakeWs:states", layerDescPath + "/Query/@typeName", dom); } public void testSingleRasterLayer() throws Exception { @@ -225,7 +222,7 @@ public void testSingleRasterLayer() throws Exception { final String layerDescPath = "/WMS_DescribeLayerResponse/LayerDescription"; assertXpathExists(layerDescPath, dom); - assertXpathEvaluatesTo("geos:fakeCoverage", layerDescPath + "/@name", dom); + assertXpathEvaluatesTo("fakeWs:fakeCoverage", layerDescPath + "/@name", dom); // no wfs attribute for a coverage layer assertXpathEvaluatesTo("", layerDescPath + "/@wfs", dom); @@ -238,7 +235,7 @@ public void testSingleRasterLayer() throws Exception { assertXpathEvaluatesTo("WCS", layerDescPath + "/@owsType", dom); assertXpathExists(layerDescPath + "/Query", dom); - assertXpathEvaluatesTo("geos:fakeCoverage", layerDescPath + "/Query/@typeName", dom); + assertXpathEvaluatesTo("fakeWs:fakeCoverage", layerDescPath + "/Query/@typeName", dom); } public void testMultipleLayers() throws Exception { @@ -255,7 +252,7 @@ public void testMultipleLayers() throws Exception { assertXpathExists(layerDescPath1, dom); assertXpathExists(layerDescPath2, dom); - assertXpathEvaluatesTo("geos:states", layerDescPath1 + "/@name", dom); - assertXpathEvaluatesTo("geos:fakeCoverage", layerDescPath2 + "/@name", dom); + assertXpathEvaluatesTo("fakeWs:states", layerDescPath1 + "/@name", dom); + assertXpathEvaluatesTo("fakeWs:fakeCoverage", layerDescPath2 + "/@name", dom); } } From cf50a5071d1b3abf495dedea535bc73d72c3a62b Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Fri, 10 Aug 2012 14:37:14 +0200 Subject: [PATCH 27/72] Fix GEOS-5259 --- .../security/usergrouprole/schemas/roles.xsd | 25 +++++++-- .../security/usergrouprole/schemas/users.xsd | 31 ++++++++--- .../org/geoserver/security/xml/roles.xsd | 25 +++++++-- .../org/geoserver/security/xml/users.xsd | 31 ++++++++--- .../org/geoserver/security/xml/roles.xml | 51 ++++++++++--------- .../org/geoserver/security/xml/users.xml | 10 ++-- 6 files changed, 119 insertions(+), 54 deletions(-) diff --git a/doc/en/user/source/security/usergrouprole/schemas/roles.xsd b/doc/en/user/source/security/usergrouprole/schemas/roles.xsd index 56fc93248e3..f7a08fc52b6 100644 --- a/doc/en/user/source/security/usergrouprole/schemas/roles.xsd +++ b/doc/en/user/source/security/usergrouprole/schemas/roles.xsd @@ -2,18 +2,35 @@ - + + + + + + + + + + + + + + + + + + - - + + - + diff --git a/doc/en/user/source/security/usergrouprole/schemas/users.xsd b/doc/en/user/source/security/usergrouprole/schemas/users.xsd index d965e5a21ee..086b97dbb10 100644 --- a/doc/en/user/source/security/usergrouprole/schemas/users.xsd +++ b/doc/en/user/source/security/usergrouprole/schemas/users.xsd @@ -2,37 +2,52 @@ - + + + + + + + + + + + + + + - + - + - + + + - + - + - - + + diff --git a/src/main/src/main/resources/org/geoserver/security/xml/roles.xsd b/src/main/src/main/resources/org/geoserver/security/xml/roles.xsd index 56fc93248e3..f7a08fc52b6 100644 --- a/src/main/src/main/resources/org/geoserver/security/xml/roles.xsd +++ b/src/main/src/main/resources/org/geoserver/security/xml/roles.xsd @@ -2,18 +2,35 @@ - + + + + + + + + + + + + + + + + + + - - + + - + diff --git a/src/main/src/main/resources/org/geoserver/security/xml/users.xsd b/src/main/src/main/resources/org/geoserver/security/xml/users.xsd index d965e5a21ee..086b97dbb10 100644 --- a/src/main/src/main/resources/org/geoserver/security/xml/users.xsd +++ b/src/main/src/main/resources/org/geoserver/security/xml/users.xsd @@ -2,37 +2,52 @@ - + + + + + + + + + + + + + + - + - + - + + + - + - + - - + + diff --git a/src/main/src/test/resources/org/geoserver/security/xml/roles.xml b/src/main/src/test/resources/org/geoserver/security/xml/roles.xml index 5b85986990e..fc388a90bef 100644 --- a/src/main/src/test/resources/org/geoserver/security/xml/roles.xml +++ b/src/main/src/test/resources/org/geoserver/security/xml/roles.xml @@ -1,6 +1,6 @@ - - + + @@ -8,28 +8,29 @@ - + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/main/src/test/resources/org/geoserver/security/xml/users.xml b/src/main/src/test/resources/org/geoserver/security/xml/users.xml index 696f7ecfa76..2fed1683d6e 100644 --- a/src/main/src/test/resources/org/geoserver/security/xml/users.xml +++ b/src/main/src/test/resources/org/geoserver/security/xml/users.xml @@ -1,5 +1,5 @@ - + @@ -9,6 +9,8 @@ 12-34-38 + + @@ -22,7 +24,5 @@ - - - - + + From 58e7a52e88d11093b842380049a72012bf5db6ae Mon Sep 17 00:00:00 2001 From: jdeolive Date: Fri, 10 Aug 2012 07:07:58 -0600 Subject: [PATCH 28/72] GEOS-5261, fixing typo checking for fallback on rule name as legend title --- .../wms/legendgraphic/BufferedImageLegendGraphicBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wms/src/main/java/org/geoserver/wms/legendgraphic/BufferedImageLegendGraphicBuilder.java b/src/wms/src/main/java/org/geoserver/wms/legendgraphic/BufferedImageLegendGraphicBuilder.java index b157016e2a6..d56dccc4a6f 100644 --- a/src/wms/src/main/java/org/geoserver/wms/legendgraphic/BufferedImageLegendGraphicBuilder.java +++ b/src/wms/src/main/java/org/geoserver/wms/legendgraphic/BufferedImageLegendGraphicBuilder.java @@ -326,7 +326,7 @@ private static BufferedImage mergeLegends(List imageStack, Rule[] if (description != null && description.getTitle() != null) { final InternationalString title = description.getTitle(); labels[i] = title.toString(); - } else if (rule.getName() == null) { + } else if (rule.getName() != null) { labels[i] = rule.getName(); } else { labels[i] = ""; From deee28ea72c83e77456287f0ae3f53474b0c0d2d Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 11 Aug 2012 17:46:30 +0200 Subject: [PATCH 29/72] [GEOS-5266] Coverage parameters may fail to be properly converted --- .../java/org/geoserver/data/util/CoverageUtils.java | 2 +- .../org/geoserver/data/util/CoverageUtilsTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/src/main/java/org/geoserver/data/util/CoverageUtils.java b/src/main/src/main/java/org/geoserver/data/util/CoverageUtils.java index 1e444e9d16d..24755a17a17 100644 --- a/src/main/src/main/java/org/geoserver/data/util/CoverageUtils.java +++ b/src/main/src/main/java/org/geoserver/data/util/CoverageUtils.java @@ -325,7 +325,7 @@ public static Object getCvParamValue(final String key, ParameterValue param, fin value = params.get(key); } } else { - final Class target = param.getClass(); + final Class target = param.getDescriptor().getValueClass(); if (key.equalsIgnoreCase("InputTransparentColor") || key.equalsIgnoreCase("OutputTransparentColor")) { if (params.get(key) != null) { diff --git a/src/main/src/test/java/org/geoserver/data/util/CoverageUtilsTest.java b/src/main/src/test/java/org/geoserver/data/util/CoverageUtilsTest.java index 1e70eb7186b..99eddaa62b4 100644 --- a/src/main/src/test/java/org/geoserver/data/util/CoverageUtilsTest.java +++ b/src/main/src/test/java/org/geoserver/data/util/CoverageUtilsTest.java @@ -21,4 +21,15 @@ public void testGetOutputTransparentColor() { assertTrue(value instanceof Color); assertEquals(Color.WHITE, value); } + + public void testMaxTiles() { + ParameterDescriptor pdescriptor = ImageMosaicFormat.MAX_ALLOWED_TILES; + ParameterValue pvalue = pdescriptor.createValue(); + String key = pdescriptor.getName().getCode(); + Map values = Collections.singletonMap(key, "1"); + Object value = CoverageUtils.getCvParamValue(key, pvalue, values); + assertTrue(value instanceof Integer); + assertEquals(new Integer(1), value); + + } } From 36e8d39a3b16de6292309f7eca5d90f191019a40 Mon Sep 17 00:00:00 2001 From: Gabriel Roldan Date: Sat, 11 Aug 2012 17:48:45 -0300 Subject: [PATCH 30/72] set gwc version to 1.3-RC5 --- src/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pom.xml b/src/pom.xml index c92a0e815cf..2efd0c3051e 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -1410,7 +1410,7 @@ 2.2-SNAPSHOT 8-SNAPSHOT - 1.3-RC4 + 1.3-RC5 3.1.1.RELEASE 3.1.0.RELEASE 1.4.12 From 8faf059522a2fc47aab7483e2c4e4f0a21d63ad6 Mon Sep 17 00:00:00 2001 From: Gabriel Roldan Date: Sat, 11 Aug 2012 18:17:47 -0300 Subject: [PATCH 31/72] declare mockito-all as a test scoped dependency to web/gwc. It wasn't needed before because it was erroneously being carried over as a transitive from gwc-wms --- src/web/gwc/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/web/gwc/pom.xml b/src/web/gwc/pom.xml index 6585b8decb0..da238926e3c 100644 --- a/src/web/gwc/pom.xml +++ b/src/web/gwc/pom.xml @@ -55,6 +55,11 @@ mockrunner test + + org.mockito + mockito-all + test + From c99adbf566bcf1610da7da387a55f873c9021f19 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sun, 12 Aug 2012 08:13:53 +0200 Subject: [PATCH 32/72] GEOS-5256 obtaining a master password during migration --- .../user/source/datadirectory/migrating.rst | 1 + doc/en/user/source/installation/upgrade.rst | 47 ++++++++ .../security/GeoServerSecurityManager.java | 112 +++++++++++++++++- .../security/impl/GeoServerRole.java | 2 +- .../AbstractSecurityServiceTest.java | 13 +- .../GeoServerSecurityManagerTest.java | 92 ++++++++++++++ .../security/GroupAdminServiceTest.java | 1 - .../AbstractAuthenticationProviderTest.java | 2 +- .../auth/AuthenticationCacheFilterTest.java | 6 +- .../auth/AuthenticationFilterTest.java | 10 +- .../impl/AbstractRoleServiceTest.java | 1 + .../impl/AbstractUserDetailsServiceTest.java | 1 + .../impl/AbstractUserGroupServiceTest.java | 1 + .../password/MasterPasswordChangeTest.java | 13 +- .../MasterPasswordChangeValidatorTest.java | 7 +- .../validation/PasswordValidatorTest.java | 2 +- .../security/cas/CasAuthenticationTest.java | 2 +- .../security/jdbc/LiveDbmsDataSecurity.java | 2 +- .../web/GeoServerWicketTestSupport.java | 5 +- .../web/passwd/MasterPasswordChangePage.java | 11 +- .../passwd/MasterPasswordChangePanelTest.java | 8 +- 21 files changed, 304 insertions(+), 35 deletions(-) rename src/main/src/test/java/org/geoserver/security/{impl => }/AbstractSecurityServiceTest.java (98%) diff --git a/doc/en/user/source/datadirectory/migrating.rst b/doc/en/user/source/datadirectory/migrating.rst index a4899db26c6..a039adbae3c 100644 --- a/doc/en/user/source/datadirectory/migrating.rst +++ b/doc/en/user/source/datadirectory/migrating.rst @@ -100,6 +100,7 @@ In order to restore the GeoServer 2.1 configuration: geoserver.jceks masterpw.xml masterpw.digest + masterpw.info auth/ filter/ masterpw/ diff --git a/doc/en/user/source/installation/upgrade.rst b/doc/en/user/source/installation/upgrade.rst index d1f492abe89..af0a6c2aad0 100644 --- a/doc/en/user/source/installation/upgrade.rst +++ b/doc/en/user/source/installation/upgrade.rst @@ -20,6 +20,53 @@ subsystem. The changes focus mostly on authentication and user management. On upgrade GeoServer will update configuration in the ``security`` directory. The specific changes are described :ref:`here `. +Obtaining a master password +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Starting with Geoserver 2.2 a master password is needed. This password is used to log in as ``root`` user and to protect the Geoserver key store. + +During the upgrade process, Geoserver tries to find a proper master password. The following rules apply + + * The default admin password ``geoserver`` is not allowed. + * The minimal length of the password is 8 characters. + +The algorithm for finding a password: + +#. Look for an existing user called ``admin``. If there is such a user and the password obeys the rules above, use it. + +#. Look for a user having the role ``ROLE_ADMINISTRATOR``. If there is such a user and the password obeys the rules above, use it. + +#. Generate a random password with 8 characters of length + +The algorithm stores a file ``masterpw.info`` into the ``security`` directory. If an existing password of a user is used, the content of this file is like + +:: + + This file was created at 2012/08/11 15:57:52 + + Master password is identical to the password of user: admin + + Test the master password by logging in as user "root" + + This file should be removed after reading !!!. + + +If a master password was generated, the content is like + +:: + + + This file was created at 2012/08/11 15:57:52 + + The generated master password is: pw?"9bWL + + Test the master password by logging in as user "root" + + This file should be removed after reading !!!. + +After reading this file, remember the master password and remove this file. + + RESTconfig security and administrative access ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java b/src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java index fe3c3ef4511..84bad837415 100644 --- a/src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java +++ b/src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java @@ -7,6 +7,7 @@ import static org.geoserver.data.util.IOUtils.xStreamPersist; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -14,13 +15,19 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.Modifier; import java.net.URL; import java.rmi.server.UID; import java.security.InvalidKeyException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -142,6 +149,7 @@ import org.springframework.security.core.userdetails.memory.UserAttributeEditor; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.context.SecurityContextPersistenceFilter; +import org.vfny.geoserver.crs.GeoserverGridShiftLocator; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; @@ -165,8 +173,11 @@ public class GeoServerSecurityManager extends ProviderManager implements Applica /** default config file name */ public static final String CONFIG_FILENAME = "config.xml"; - /** master password cpnfig file name */ + /** master password config file name */ public static final String MASTER_PASSWD_CONFIG_FILENAME = "masterpw.xml"; + + /** master password info file name */ + public static final String MASTER_PASSWD_INFO_FILENAME = "masterpw.info"; /** master password digest file name */ public static final String MASTER_PASSWD_DIGEST_FILENAME = "masterpw.digest"; @@ -283,6 +294,16 @@ public void setApplicationContext(ApplicationContext appContext) throws BeansExc @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextLoadedEvent) { + + try { + File masterPasswordInfo = new File(getSecurityRoot(), MASTER_PASSWD_INFO_FILENAME); + if (masterPasswordInfo.exists()) { + LOGGER.warning(masterPasswordInfo.getCanonicalPath()+" is a security risk. Please read this file and remove it afterward"); + } + } catch (Exception e1) { + throw new RuntimeException(e1); + } + // migrate from old security config try { migrateIfNecessary(); @@ -1655,6 +1676,89 @@ void fireChanged() { } } + + /** + * @return the master password used for the migration + * @throws Exception + */ + char[] extractMasterPasswordForMigration(Properties props) throws Exception { + + + Map candidates = new HashMap(); + String defaultPasswordAsString = new String(MASTER_PASSWD_DEFAULT); + + if (props!=null) { + //load user.properties populate the services + + UserAttributeEditor configAttribEd = new UserAttributeEditor(); + + for (Iterator iter = props.keySet().iterator(); iter.hasNext();) { + String username = (String) iter.next(); + + configAttribEd.setAsText(props.getProperty(username)); + UserAttribute attr = (UserAttribute) configAttribEd.getValue(); + if (attr == null) continue; + + // The master password policy is not yet available, the default is to + // have a minimum of 8 chars --> all passwords shorter than 8 chars + // are no candidates + if (attr.getPassword()==null || attr.getPassword().length() <8 ) + continue; + + // The default password is not allowed + if (defaultPasswordAsString.equals(attr.getPassword())) + continue; + + // the user named "admin" having a non default password is the primary candiate + if (GeoServerUser.ADMIN_USERNAME.equals(username)) { + candidates.put(GeoServerUser.ADMIN_USERNAME,attr.getPassword()); + continue; + } + + // other users having the amin role are secondary candidates + if (attr.getAuthorities().contains(GeoServerRole.ADMIN_ROLE)) { + candidates.put(username,attr.getPassword()); + } + } + } + + String username = GeoServerUser.ADMIN_USERNAME; + String masterPW=candidates.get(username); + if (masterPW==null && candidates.size()>0) { + username = candidates.keySet().iterator().next(); + masterPW=candidates.get(username); + } + + String message = null; + + if (masterPW!=null) { + message="Master password is identical to the password of user: "+username; + } else { + masterPW = new String(getRandomPassworddProvider().getRandomPassword(8)); + message="The generated master password is: "+masterPW; + } + + File info = new File(getSecurityRoot(),MASTER_PASSWD_INFO_FILENAME); + BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(info))); + DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + w.write("This file was created at "+dateFormat.format(new Date())); + w.newLine(); + w.newLine(); + w.write(message); + w.newLine(); + w.newLine(); + w.write("Test the master password by logging in as user \"root\""); + w.newLine(); + w.newLine(); + w.write("This file should be removed after reading !!!."); + w.newLine(); + w.close(); + + LOGGER.info("Information regarding the master password is in: "+ info.getCanonicalPath()); + return masterPW.toCharArray(); + } + + /* * converts an old security configuration to the new */ @@ -1693,7 +1797,11 @@ void migrateIfNecessary() throws Exception{ //save out the default master password MasterPasswordProvider mpProvider = loadMasterPasswordProvider(mpProviderConfig.getName()); - mpProvider.setMasterPassword(MASTER_PASSWD_DEFAULT); + File propFile = new File(getSecurityRoot(), "users.properties"); + Properties userprops=null; + if (propFile.exists()) + userprops = Util.loadPropertyFile(propFile); + mpProvider.setMasterPassword(extractMasterPasswordForMigration(userprops)); } MasterPasswordConfig mpConfig = new MasterPasswordConfig(); diff --git a/src/main/src/main/java/org/geoserver/security/impl/GeoServerRole.java b/src/main/src/main/java/org/geoserver/security/impl/GeoServerRole.java index b6bae7ff4fc..705f2e0ab08 100644 --- a/src/main/src/main/java/org/geoserver/security/impl/GeoServerRole.java +++ b/src/main/src/main/java/org/geoserver/security/impl/GeoServerRole.java @@ -135,7 +135,7 @@ public boolean equals(Object obj) { if (obj instanceof GrantedAuthority && getUserName()==null) { if (obj instanceof GeoServerRole ==false) - equalsWithoutUserName(obj); + return equalsWithoutUserName(obj); } if (obj instanceof GeoServerRole) { diff --git a/src/main/src/test/java/org/geoserver/security/impl/AbstractSecurityServiceTest.java b/src/main/src/test/java/org/geoserver/security/AbstractSecurityServiceTest.java similarity index 98% rename from src/main/src/test/java/org/geoserver/security/impl/AbstractSecurityServiceTest.java rename to src/main/src/test/java/org/geoserver/security/AbstractSecurityServiceTest.java index 77b35a54dd9..0eeaeff3605 100644 --- a/src/main/src/test/java/org/geoserver/security/impl/AbstractSecurityServiceTest.java +++ b/src/main/src/test/java/org/geoserver/security/AbstractSecurityServiceTest.java @@ -3,7 +3,7 @@ * application directory. */ -package org.geoserver.security.impl; +package org.geoserver.security; import java.io.File; @@ -17,6 +17,9 @@ import org.geoserver.security.GeoServerRoleStore; import org.geoserver.security.GeoServerUserGroupService; import org.geoserver.security.GeoServerUserGroupStore; +import org.geoserver.security.impl.GeoServerRole; +import org.geoserver.security.impl.GeoServerUser; +import org.geoserver.security.impl.GeoServerUserGroup; import org.geoserver.security.password.GeoServerDigestPasswordEncoder; import org.geoserver.security.password.GeoServerEmptyPasswordEncoder; import org.geoserver.security.password.GeoServerMultiplexingPasswordEncoder; @@ -586,4 +589,12 @@ protected GeoServerEmptyPasswordEncoder getEmptyEncoder() { return getSecurityManager().loadPasswordEncoder(GeoServerEmptyPasswordEncoder.class); } + /** + * Accessor for the geoserver master password. + * @return + */ + protected String getMasterPassword() { + return new String(getSecurityManager().getMasterPassword()); + } + } diff --git a/src/main/src/test/java/org/geoserver/security/GeoServerSecurityManagerTest.java b/src/main/src/test/java/org/geoserver/security/GeoServerSecurityManagerTest.java index d41ce9b799a..e2b2e2af693 100644 --- a/src/main/src/test/java/org/geoserver/security/GeoServerSecurityManagerTest.java +++ b/src/main/src/test/java/org/geoserver/security/GeoServerSecurityManagerTest.java @@ -1,9 +1,14 @@ package org.geoserver.security; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; import java.util.Arrays; import java.util.List; +import java.util.Properties; import org.geoserver.security.impl.GeoServerRole; +import org.geoserver.security.impl.GeoServerUser; import org.springframework.security.authentication.TestingAuthenticationToken; public class GeoServerSecurityManagerTest extends GeoServerSecurityTestSupport { @@ -18,4 +23,91 @@ public void testAdminRole() throws Exception { } + + public void testMasterPasswordForMigration() throws Exception { + + // simulate no user.properties file + GeoServerSecurityManager secMgr = getSecurityManager(); + char[] generatedPW= secMgr.extractMasterPasswordForMigration(null); + assertTrue(generatedPW.length==8); + assertTrue(masterPWInfoFileContains(new String(generatedPW))); + //dumpPWInfoFile(); + + Properties props = new Properties(); + String adminUser="user1"; + String noAdminUser="user2"; + + // check all users with default password + String defaultMasterePassword = new String(GeoServerSecurityManager.MASTER_PASSWD_DEFAULT); + props.put(GeoServerUser.ADMIN_USERNAME, defaultMasterePassword+","+GeoServerRole.ADMIN_ROLE); + props.put(adminUser, defaultMasterePassword+","+GeoServerRole.ADMIN_ROLE); + props.put(noAdminUser, defaultMasterePassword+",ROLE_WFS"); + + generatedPW= secMgr.extractMasterPasswordForMigration(props); + assertTrue(generatedPW.length==8); + assertTrue(masterPWInfoFileContains(new String(generatedPW))); + assertFalse(masterPWInfoFileContains(GeoServerUser.ADMIN_USERNAME)); + assertFalse(masterPWInfoFileContains(adminUser)); + assertFalse(masterPWInfoFileContains(noAdminUser)); + //dumpPWInfoFile(); + + // valid master password for noadminuser + props.put(noAdminUser, "validPassword"+",ROLE_WFS"); + generatedPW= secMgr.extractMasterPasswordForMigration(props); + assertTrue(generatedPW.length==8); + assertTrue(masterPWInfoFileContains(new String(generatedPW))); + + // password to short for adminuser + props.put(adminUser, "abc"+","+GeoServerRole.ADMIN_ROLE); + generatedPW= secMgr.extractMasterPasswordForMigration(props); + assertTrue(generatedPW.length==8); + assertTrue(masterPWInfoFileContains(new String(generatedPW))); + + // valid password for user having admin role + + String validPassword = "validPassword"; + props.put(adminUser, validPassword+","+GeoServerRole.ADMIN_ROLE); + generatedPW= secMgr.extractMasterPasswordForMigration(props); + assertEquals(validPassword, new String(generatedPW)); + assertFalse(masterPWInfoFileContains(validPassword)); + assertTrue(masterPWInfoFileContains(adminUser)); + //dumpPWInfoFile(); + + // valid password for "admin" user + props.put(GeoServerUser.ADMIN_USERNAME, validPassword+","+GeoServerRole.ADMIN_ROLE); + generatedPW= secMgr.extractMasterPasswordForMigration(props); + assertEquals(validPassword, new String(generatedPW)); + assertFalse(masterPWInfoFileContains(validPassword)); + assertTrue(masterPWInfoFileContains(GeoServerUser.ADMIN_USERNAME)); + //dumpPWInfoFile(); + + } + + void dumpPWInfoFile() throws Exception { + File infoFile = new File(getSecurityManager().getSecurityRoot(),GeoServerSecurityManager.MASTER_PASSWD_INFO_FILENAME); + + BufferedReader bf = new BufferedReader(new FileReader(infoFile)); + String line; + while (( line = bf.readLine()) != null) { + System.out.println(line); + } + bf.close(); + + } + + + boolean masterPWInfoFileContains(String searchString) throws Exception { + File infoFile = new File(getSecurityManager().getSecurityRoot(),GeoServerSecurityManager.MASTER_PASSWD_INFO_FILENAME); + + BufferedReader bf = new BufferedReader(new FileReader(infoFile)); + String line; + while (( line = bf.readLine()) != null) { + if (line.indexOf(searchString)!= -1) { + bf.close(); + return true; + } + } + bf.close(); + return false; + } } diff --git a/src/main/src/test/java/org/geoserver/security/GroupAdminServiceTest.java b/src/main/src/test/java/org/geoserver/security/GroupAdminServiceTest.java index b12d03d298c..ba0b5551ef0 100644 --- a/src/main/src/test/java/org/geoserver/security/GroupAdminServiceTest.java +++ b/src/main/src/test/java/org/geoserver/security/GroupAdminServiceTest.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.util.Collections; -import org.geoserver.security.impl.AbstractSecurityServiceTest; import org.geoserver.security.impl.GeoServerRole; import org.geoserver.security.impl.GeoServerUser; import org.geoserver.security.impl.GeoServerUserGroup; diff --git a/src/main/src/test/java/org/geoserver/security/auth/AbstractAuthenticationProviderTest.java b/src/main/src/test/java/org/geoserver/security/auth/AbstractAuthenticationProviderTest.java index aef49f57500..30bb3c18f0b 100644 --- a/src/main/src/test/java/org/geoserver/security/auth/AbstractAuthenticationProviderTest.java +++ b/src/main/src/test/java/org/geoserver/security/auth/AbstractAuthenticationProviderTest.java @@ -26,6 +26,7 @@ import java.util.Set; import org.geoserver.platform.GeoServerExtensions; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.GeoServerAuthenticationProvider; import org.geoserver.security.GeoServerRoleService; import org.geoserver.security.GeoServerRoleStore; @@ -43,7 +44,6 @@ import org.geoserver.security.filter.GeoServerCompositeFilter; import org.geoserver.security.filter.GeoServerLogoutFilter; import org.geoserver.security.filter.GeoServerUserNamePasswordAuthenticationFilter; -import org.geoserver.security.impl.AbstractSecurityServiceTest; import org.geoserver.security.impl.DigestAuthUtils; import org.geoserver.security.impl.GeoServerRole; import org.geoserver.security.impl.GeoServerUser; diff --git a/src/main/src/test/java/org/geoserver/security/auth/AuthenticationCacheFilterTest.java b/src/main/src/test/java/org/geoserver/security/auth/AuthenticationCacheFilterTest.java index 2905b072ed5..d9f425a7b99 100644 --- a/src/main/src/test/java/org/geoserver/security/auth/AuthenticationCacheFilterTest.java +++ b/src/main/src/test/java/org/geoserver/security/auth/AuthenticationCacheFilterTest.java @@ -188,7 +188,7 @@ public void testBasicAuth() throws Exception{ chain = new MockFilterChain(); request.addHeader("Authorization", "Basic " + - new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":geoserver").getBytes()))); + new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":"+getMasterPassword()).getBytes()))); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); auth = getAuth(GeoServerUser.ROOT_USERNAME, "geoserver",null,null); @@ -543,7 +543,7 @@ public void testDigestAuth() throws Exception{ response= new MockHttpServletResponse(); chain = new MockFilterChain(); - headerValue=clientDigestString(tmp, GeoServerUser.ROOT_USERNAME, "geoserver", request.getMethod()); + headerValue=clientDigestString(tmp, GeoServerUser.ROOT_USERNAME, getMasterPassword(), request.getMethod()); request.addHeader("Authorization", headerValue); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); @@ -707,7 +707,7 @@ public void testBasicAuthWithRememberMe() throws Exception{ request.addHeader("Authorization", "Basic " + - new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":geoserver").getBytes()))); + new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":"+getMasterPassword()).getBytes()))); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); auth = getAuth(testFilterName5, GeoServerUser.ROOT_USERNAME, null,null); diff --git a/src/main/src/test/java/org/geoserver/security/auth/AuthenticationFilterTest.java b/src/main/src/test/java/org/geoserver/security/auth/AuthenticationFilterTest.java index dc0896f2dbd..e32affa8fb9 100644 --- a/src/main/src/test/java/org/geoserver/security/auth/AuthenticationFilterTest.java +++ b/src/main/src/test/java/org/geoserver/security/auth/AuthenticationFilterTest.java @@ -155,7 +155,7 @@ public void testBasicAuth() throws Exception{ chain = new MockFilterChain(); request.addHeader("Authorization", "Basic " + - new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":geoserver").getBytes()))); + new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":"+getMasterPassword()).getBytes()))); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); ctx = (SecurityContext)request.getSession(true).getAttribute( @@ -550,7 +550,7 @@ public void testDigestAuth() throws Exception{ response= new MockHttpServletResponse(); chain = new MockFilterChain(); - headerValue=clientDigestString(tmp, GeoServerUser.ROOT_USERNAME, "geoserver", request.getMethod()); + headerValue=clientDigestString(tmp, GeoServerUser.ROOT_USERNAME, getMasterPassword(), request.getMethod()); request.addHeader("Authorization", headerValue); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); @@ -712,7 +712,7 @@ public void testBasicAuthWithRememberMe() throws Exception{ request.addHeader("Authorization", "Basic " + - new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":geoserver").getBytes()))); + new String(Base64.encodeBytes((GeoServerUser.ROOT_USERNAME+":"+getMasterPassword()).getBytes()))); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); ctx = (SecurityContext)request.getSession(true).getAttribute( @@ -873,7 +873,7 @@ public void testFormLogin() throws Exception { chain = new MockFilterChain(); request.setMethod("POST"); request.setupAddParameter(config.getUsernameParameterName(), GeoServerUser.ROOT_USERNAME); - request.setupAddParameter(config.getPasswordParameterName(), "geoserver"); + request.setupAddParameter(config.getPasswordParameterName(), getMasterPassword()); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); assertTrue(response.wasRedirectSent()); @@ -1040,7 +1040,7 @@ public void testFormLoginWithRememberMe() throws Exception{ chain = new MockFilterChain(); request.setMethod("POST"); request.setupAddParameter(config.getUsernameParameterName(), GeoServerUser.ROOT_USERNAME); - request.setupAddParameter(config.getPasswordParameterName(), "geoserver"); + request.setupAddParameter(config.getPasswordParameterName(), getMasterPassword()); getProxy().doFilter(request, response, chain); assertEquals(HttpServletResponse.SC_OK, response.getErrorCode()); assertTrue(response.wasRedirectSent()); diff --git a/src/main/src/test/java/org/geoserver/security/impl/AbstractRoleServiceTest.java b/src/main/src/test/java/org/geoserver/security/impl/AbstractRoleServiceTest.java index 725c3bdc42d..86c24e7ca28 100644 --- a/src/main/src/test/java/org/geoserver/security/impl/AbstractRoleServiceTest.java +++ b/src/main/src/test/java/org/geoserver/security/impl/AbstractRoleServiceTest.java @@ -10,6 +10,7 @@ import junit.framework.Assert; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.GeoServerRoleService; import org.geoserver.security.GeoServerRoleStore; diff --git a/src/main/src/test/java/org/geoserver/security/impl/AbstractUserDetailsServiceTest.java b/src/main/src/test/java/org/geoserver/security/impl/AbstractUserDetailsServiceTest.java index 6b3cf3355b1..36c3877fc41 100644 --- a/src/main/src/test/java/org/geoserver/security/impl/AbstractUserDetailsServiceTest.java +++ b/src/main/src/test/java/org/geoserver/security/impl/AbstractUserDetailsServiceTest.java @@ -12,6 +12,7 @@ import junit.framework.Assert; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.GeoServerRoleService; import org.geoserver.security.GeoServerRoleStore; import org.geoserver.security.GeoServerUserGroupService; diff --git a/src/main/src/test/java/org/geoserver/security/impl/AbstractUserGroupServiceTest.java b/src/main/src/test/java/org/geoserver/security/impl/AbstractUserGroupServiceTest.java index a667c72fd98..994a3a395f8 100644 --- a/src/main/src/test/java/org/geoserver/security/impl/AbstractUserGroupServiceTest.java +++ b/src/main/src/test/java/org/geoserver/security/impl/AbstractUserGroupServiceTest.java @@ -7,6 +7,7 @@ import junit.framework.Assert; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.GeoServerUserGroupService; import org.geoserver.security.GeoServerUserGroupStore; import org.geoserver.security.config.SecurityUserGroupServiceConfig; diff --git a/src/main/src/test/java/org/geoserver/security/password/MasterPasswordChangeTest.java b/src/main/src/test/java/org/geoserver/security/password/MasterPasswordChangeTest.java index a6a21b61f79..b82105740a2 100644 --- a/src/main/src/test/java/org/geoserver/security/password/MasterPasswordChangeTest.java +++ b/src/main/src/test/java/org/geoserver/security/password/MasterPasswordChangeTest.java @@ -34,7 +34,8 @@ protected String[] getSpringContextLocations() { public void testMasterPasswordChange() throws Exception { // keytool -storepasswd -new geoserver1 -storepass geoserver -storetype jceks -keystore geoserver.jks - assertEquals(new String(MASTER_PASSWD_DEFAULT), getMasterPassword()); + + String masterPWAsString = getMasterPassword(); MasterPasswordConfig config = getSecurityManager().getMasterPasswordConfig(); URLMasterPasswordProviderConfig mpConfig = (URLMasterPasswordProviderConfig) @@ -62,7 +63,7 @@ public void testMasterPasswordChange() throws Exception { config = getSecurityManager().getMasterPasswordConfig(); config.setProviderName(mpConfig.getName()); getSecurityManager().saveMasterPasswordConfig( - config, MASTER_PASSWD_DEFAULT, "geoserver1".toCharArray(), "geoserver1".toCharArray()); + config, masterPWAsString.toCharArray(), "geoserver1".toCharArray(), "geoserver1".toCharArray()); assertEquals("geoserver1", getMasterPassword()); getSecurityManager().getKeyStoreProvider().getConfigPasswordKey(); @@ -108,22 +109,22 @@ public void testMasterPasswordChange() throws Exception { } public void testRootLoginAfterMasterPasswdChange() throws Exception { - assertEquals(new String(MASTER_PASSWD_DEFAULT), getMasterPassword()); + String masterPWAsString = getMasterPassword(); GeoServerRootAuthenticationProvider authProvider = new GeoServerRootAuthenticationProvider(); authProvider.setSecurityManager(getSecurityManager()); - Authentication auth = new UsernamePasswordAuthenticationToken("root", "geoserver"); + Authentication auth = new UsernamePasswordAuthenticationToken("root", masterPWAsString); auth = authProvider.authenticate(auth); assertTrue(auth.isAuthenticated()); MasterPasswordConfig config = getSecurityManager().getMasterPasswordConfig(); - getSecurityManager().saveMasterPasswordConfig(config, MASTER_PASSWD_DEFAULT, + getSecurityManager().saveMasterPasswordConfig(config, masterPWAsString.toCharArray(), "geoserver1".toCharArray(), "geoserver1".toCharArray()); assertEquals("geoserver1", getMasterPassword()); - auth = new UsernamePasswordAuthenticationToken("root", "geoserver"); + auth = new UsernamePasswordAuthenticationToken("root", masterPWAsString); assertNull(authProvider.authenticate(auth)); assertFalse(auth.isAuthenticated()); diff --git a/src/main/src/test/java/org/geoserver/security/validation/MasterPasswordChangeValidatorTest.java b/src/main/src/test/java/org/geoserver/security/validation/MasterPasswordChangeValidatorTest.java index 76c13ef866e..02a880c42b7 100644 --- a/src/main/src/test/java/org/geoserver/security/validation/MasterPasswordChangeValidatorTest.java +++ b/src/main/src/test/java/org/geoserver/security/validation/MasterPasswordChangeValidatorTest.java @@ -2,15 +2,14 @@ import java.net.URL; -import org.geoserver.security.GeoServerSecurityManager; -import org.geoserver.security.impl.AbstractSecurityServiceTest; +import org.geoserver.security.GeoServerSecurityTestSupport; import org.geoserver.security.password.MasterPasswordChangeRequest; import org.geoserver.security.password.MasterPasswordProviderException; import org.geoserver.security.password.URLMasterPasswordProvider; import org.geoserver.security.password.URLMasterPasswordProviderConfig; import org.geoserver.security.password.URLMasterPasswordProviderException; -public class MasterPasswordChangeValidatorTest extends AbstractSecurityServiceTest { +public class MasterPasswordChangeValidatorTest extends GeoServerSecurityTestSupport { MasterPasswordChangeValidator validator; @@ -113,7 +112,7 @@ public void testValidator() throws Exception{ MasterPasswordChangeRequest r = new MasterPasswordChangeRequest(); checkCurrentPassword(r); - r.setCurrentPassword(GeoServerSecurityManager.MASTER_PASSWD_DEFAULT); + r.setCurrentPassword(getMasterPassword().toCharArray()); checkConfirmationPassword(r); r.setConfirmPassword("abc".toCharArray()); diff --git a/src/main/src/test/java/org/geoserver/security/validation/PasswordValidatorTest.java b/src/main/src/test/java/org/geoserver/security/validation/PasswordValidatorTest.java index 37dad1b26c0..2854543ac4f 100644 --- a/src/main/src/test/java/org/geoserver/security/validation/PasswordValidatorTest.java +++ b/src/main/src/test/java/org/geoserver/security/validation/PasswordValidatorTest.java @@ -8,8 +8,8 @@ import static org.geoserver.security.validation.PasswordPolicyException.NO_UPPERCASE; import static org.geoserver.security.validation.PasswordPolicyException.RESERVED_PREFIX_$1; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.config.PasswordPolicyConfig; -import org.geoserver.security.impl.AbstractSecurityServiceTest; public class PasswordValidatorTest extends AbstractSecurityServiceTest { diff --git a/src/security/cas/src/test/java/org/geoserver/security/cas/CasAuthenticationTest.java b/src/security/cas/src/test/java/org/geoserver/security/cas/CasAuthenticationTest.java index ab340527858..f78fb203d91 100644 --- a/src/security/cas/src/test/java/org/geoserver/security/cas/CasAuthenticationTest.java +++ b/src/security/cas/src/test/java/org/geoserver/security/cas/CasAuthenticationTest.java @@ -13,6 +13,7 @@ import org.geoserver.data.test.TestData; import org.geoserver.platform.GeoServerExtensions; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.GeoServerSecurityFilterChain; import org.geoserver.security.auth.AbstractAuthenticationProviderTest; import org.geoserver.security.auth.TestingAuthenticationCache; @@ -20,7 +21,6 @@ import org.geoserver.security.config.PreAuthenticatedUserNameFilterConfig.RoleSource; import org.geoserver.security.filter.GeoServerExceptionTranslationFilter; import org.geoserver.security.filter.GeoServerUserNamePasswordAuthenticationFilter; -import org.geoserver.security.impl.AbstractSecurityServiceTest; import org.geoserver.security.impl.GeoServerRole; import org.geoserver.security.impl.GeoServerUser; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; diff --git a/src/security/jdbc/src/test/java/org/geoserver/security/jdbc/LiveDbmsDataSecurity.java b/src/security/jdbc/src/test/java/org/geoserver/security/jdbc/LiveDbmsDataSecurity.java index 027f311b3dd..9a5bc134947 100644 --- a/src/security/jdbc/src/test/java/org/geoserver/security/jdbc/LiveDbmsDataSecurity.java +++ b/src/security/jdbc/src/test/java/org/geoserver/security/jdbc/LiveDbmsDataSecurity.java @@ -16,7 +16,7 @@ import org.geoserver.data.test.LiveDbmsData; import org.geoserver.data.util.IOUtils; -import org.geoserver.security.impl.AbstractSecurityServiceTest; +import org.geoserver.security.AbstractSecurityServiceTest; import org.geoserver.security.impl.Util; public class LiveDbmsDataSecurity extends LiveDbmsData { diff --git a/src/web/core/src/test/java/org/geoserver/web/GeoServerWicketTestSupport.java b/src/web/core/src/test/java/org/geoserver/web/GeoServerWicketTestSupport.java index 557eccbd9ec..6103919ca93 100644 --- a/src/web/core/src/test/java/org/geoserver/web/GeoServerWicketTestSupport.java +++ b/src/web/core/src/test/java/org/geoserver/web/GeoServerWicketTestSupport.java @@ -11,15 +11,14 @@ import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.util.tester.FormTester; import org.apache.wicket.util.tester.WicketTester; -import org.geoserver.security.impl.GeoServerRole; -import org.geoserver.test.GeoServerTestSupport; +import org.geoserver.security.GeoServerSecurityTestSupport; import org.geoserver.web.wicket.WicketHierarchyPrinter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -public abstract class GeoServerWicketTestSupport extends GeoServerTestSupport { +public abstract class GeoServerWicketTestSupport extends GeoServerSecurityTestSupport { public static WicketTester tester; public void oneTimeSetUp() throws Exception { diff --git a/src/web/security/src/main/java/org/geoserver/security/web/passwd/MasterPasswordChangePage.java b/src/web/security/src/main/java/org/geoserver/security/web/passwd/MasterPasswordChangePage.java index f9c910c40e5..2c5c81e75fc 100644 --- a/src/web/security/src/main/java/org/geoserver/security/web/passwd/MasterPasswordChangePage.java +++ b/src/web/security/src/main/java/org/geoserver/security/web/passwd/MasterPasswordChangePage.java @@ -47,12 +47,17 @@ public MasterPasswordChangePage() { @Override public void onSubmit() { Form f = getForm(); + // @Justin, we cannot use getDefaultModelObjectAsString() because of special chars. + // example: The password "mcrmcr&1" is converted to "mcrmcr&1". String currPasswd = - f.get("currentPassword").getDefaultModelObjectAsString(); + //f.get("currentPassword").getDefaultModelObjectAsString(); + (String) f.get("currentPassword").getDefaultModelObject(); String newPasswd = - f.get("newPassword").getDefaultModelObjectAsString(); + //f.get("newPassword").getDefaultModelObjectAsString(); + (String) f.get("newPassword").getDefaultModelObject(); String newPasswdConfirm = - f.get("newPasswordConfirm").getDefaultModelObjectAsString(); + // f.get("newPasswordConfirm").getDefaultModelObjectAsString(); + (String) f.get("newPasswordConfirm").getDefaultModelObject(); MasterPasswordConfig mpConfig = (MasterPasswordConfig) getForm().getModelObject(); try { diff --git a/src/web/security/src/test/java/org/geoserver/security/web/passwd/MasterPasswordChangePanelTest.java b/src/web/security/src/test/java/org/geoserver/security/web/passwd/MasterPasswordChangePanelTest.java index 5505905a215..ed70cdda8f7 100644 --- a/src/web/security/src/test/java/org/geoserver/security/web/passwd/MasterPasswordChangePanelTest.java +++ b/src/web/security/src/test/java/org/geoserver/security/web/passwd/MasterPasswordChangePanelTest.java @@ -30,7 +30,9 @@ public void testBadCurrentPassword() throws Exception { } public void testPasswordViolatesPolicy() throws Exception { - ft.setValue("currentPassword", "geoserver"); + String mpw = getMasterPassword(); + System.out.println("testPasswordViolatesPolicy: "+mpw); + ft.setValue("currentPassword", mpw); ft.setValue("newPassword", "bar"); ft.setValue("newPasswordConfirm", "bar"); ft.submit("save"); @@ -38,7 +40,9 @@ public void testPasswordViolatesPolicy() throws Exception { } public void testPasswordChange() throws Exception { - ft.setValue("currentPassword", "geoserver"); + String mpw = getMasterPassword(); + System.out.println("testPasswordChange: "+mpw); + ft.setValue("currentPassword", mpw); ft.setValue("newPassword", "Foobar2012"); ft.setValue("newPasswordConfirm", "Foobar2012"); ft.submit("save"); From f599035cd6421c83c90a1de2633ea1c4f65256f1 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 15 Aug 2012 15:31:08 -0700 Subject: [PATCH 33/72] Edit process metadata --- .../geoserver/wps/gs/GeorectifyCoverage.java | 26 +++++++++---------- .../org/geoserver/wps/gs/GetFullCoverage.java | 8 +++--- .../org/geoserver/wps/gs/ImportProcess.java | 18 ++++++------- .../org/geoserver/wps/gs/StoreCoverage.java | 6 ++--- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GeorectifyCoverage.java b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GeorectifyCoverage.java index 1e19c819b1a..1197d789f00 100644 --- a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GeorectifyCoverage.java +++ b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GeorectifyCoverage.java @@ -60,7 +60,7 @@ * @author Daniele Romagnoli, GeoSolutions SAS * @author Andrea Aime, GeoSolutions SAS */ -@DescribeProcess(title = "GeorectifyCoverage", description = "Process which allows to georectify a coverage through GCPs using gdal_warp") +@DescribeProcess(title = "Georectify Coverage", description = "Georectifies a raster via Ground Control Points using gdal_warp") public class GeorectifyCoverage implements GSProcess { private final static Pattern GCP_PATTERN = Pattern @@ -87,20 +87,20 @@ public GeorectifyCoverage() { } @DescribeResults({ - @DescribeResult(name = "result", description = "The GridCoverage2D coming from the georectification process", type=GridCoverage2D.class), - @DescribeResult(name = "path", description = "The server side path to the generated coverage", type=String.class) + @DescribeResult(name = "result", description = "Georectified raster", type=GridCoverage2D.class), + @DescribeResult(name = "path", description = "Pathname of the generated raster on the server", type=String.class) }) public Map execute( - @DescribeParameter(name = "data", description = "The input raster to transform") GridCoverage2D coverage, - @DescribeParameter(name = "gcp", description = "The Ground Control Points list in the form ") String gcps, - @DescribeParameter(name = "bbox", description = "The destination bounding box", min = 0) Envelope bbox, - @DescribeParameter(name = "targetCRS", description = "The destination coordinate refence system") CoordinateReferenceSystem crs, - @DescribeParameter(name = "width", description = "The final image width", min = 0) Integer width, - @DescribeParameter(name = "height", description = "The final image height", min = 0) Integer height, - @DescribeParameter(name = "warpOrder", min = 0, description = "The order of the warping polynomial (optional)") Integer warpOrder, - @DescribeParameter(name = "transparent", min = 0, description = "Force the output image to have transparent background") Boolean transparent, - @DescribeParameter(name = "store", min = 0, description = "Don't remove the output file once done") Boolean store, - @DescribeParameter(name = "outputPath", min = 0, description = "Full path where the output file has to be stored") String outputPath) + @DescribeParameter(name = "data", description = "Input raster") GridCoverage2D coverage, + @DescribeParameter(name = "gcp", description = "List of Ground control points. Points are specified as [x,y] or [x,y,z].") String gcps, + @DescribeParameter(name = "bbox", description = "Bounding box for output", min = 0) Envelope bbox, + @DescribeParameter(name = "targetCRS", description = "Coordinate reference system to use for the output raster") CoordinateReferenceSystem crs, + @DescribeParameter(name = "width", description = "Width of output raster in pixels", min = 0) Integer width, + @DescribeParameter(name = "height", description = "Height of output raster in pixels", min = 0) Integer height, + @DescribeParameter(name = "warpOrder", min = 0, description = "Order of the warping polynomial (1 to 3)") Integer warpOrder, + @DescribeParameter(name = "transparent", min = 0, description = "Force output to have transparent background") Boolean transparent, + @DescribeParameter(name = "store", min = 0, description = "Indicates whether to keep the output file after processing") Boolean store, + @DescribeParameter(name = "outputPath", min = 0, description = "Pathname where the output file is stored") String outputPath) throws IOException { GeoTiffReader reader = null; diff --git a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GetFullCoverage.java b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GetFullCoverage.java index e2d4f9d8375..f54c6651d9f 100644 --- a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GetFullCoverage.java +++ b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/GetFullCoverage.java @@ -28,7 +28,7 @@ * @author Daniele Romagnoli, GeoSolutions SAS * @author Andrea Aime, GeoSolutions SAS */ -@DescribeProcess(title = "GetFullCoverage", description = "Returns a full coverage from the catalog, in its native form") +@DescribeProcess(title = "GetFullCoverage", description = "Returns a raster from the catalog, with optional filtering") public class GetFullCoverage implements GSProcess { private Catalog catalog; @@ -37,10 +37,10 @@ public GetFullCoverage(Catalog catalog) { this.catalog = catalog; } - @DescribeResult(name = "result", description = "The grid coverage", type = GridCoverage2D.class) + @DescribeResult(name = "result", description = "Output raster", type = GridCoverage2D.class) public GridCoverage2D execute( - @DescribeParameter(name = "name", description = "The grid coverage name, possibly fully qualified (workspace:name)") String name, - @DescribeParameter(name = "filter", description = "An eventual filter over the data", min = 0) Filter filter) + @DescribeParameter(name = "name", description = "Name of raster, optionally fully qualified (workspace:name)") String name, + @DescribeParameter(name = "filter", description = "Filter to use on the raster data", min = 0) Filter filter) throws IOException { CoverageInfo ci = catalog.getCoverageByName(name); if (ci == null) { diff --git a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/ImportProcess.java b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/ImportProcess.java index ff63fb86a69..92f0a8fe43d 100644 --- a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/ImportProcess.java +++ b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/ImportProcess.java @@ -48,7 +48,7 @@ * @author Andrea Aime - OpenGeo * */ -@DescribeProcess(title = "Catalog import", description = "Imports the provided feature collection into the catalog") +@DescribeProcess(title = "Import to Catalog", description = "Imports a feature collection into the catalog") public class ImportProcess implements GSProcess { static final Logger LOGGER = Logging.getLogger(ImportProcess.class); @@ -59,15 +59,15 @@ public ImportProcess(Catalog catalog) { this.catalog = catalog; } - @DescribeResult(name = "layerName", description = "The qualified name of the created layer (workspace:name)") + @DescribeResult(name = "layerName", description = "Name of the new featuretype, with workspace") public String execute( - @DescribeParameter(name = "features", description = "The features that will make up the new GeoServer layer") SimpleFeatureCollection features, - @DescribeParameter(name = "workspace", min = 0, description = "The target workspace (the default one will be used if omitted)") String workspace, - @DescribeParameter(name = "store", min = 0, description = "The target store (the workspace default one will be used if omitted)") String store, - @DescribeParameter(name = "name", min = 0, description = "The name of the layer to be created (if missing the name of the features contained in the collection will be used") String name, - @DescribeParameter(name = "srs", min = 0, description = "The target coordinate reference system (the feature collection one will be analyzed and used if possible)") CoordinateReferenceSystem srs, - @DescribeParameter(name = "srsHandling", min = 0, description = "The desired SRS handling, FORCE_DECLARED will be used if not specified") ProjectionPolicy srsHandling, - @DescribeParameter(name = "styleName", min = 0, description = "The name of the style to be used for the layer. If missing a default style will be chosen according to the type of geometries contained in the collection") String styleName) + @DescribeParameter(name = "features", description = "Input feature collection") SimpleFeatureCollection features, + @DescribeParameter(name = "workspace", min = 0, description = "Target workspace (default is the system default)") String workspace, + @DescribeParameter(name = "store", min = 0, description = "Target store (default is the workspace default)") String store, + @DescribeParameter(name = "name", min = 0, description = "Name of the new featuretype (default is the name of the features in the collection)") String name, + @DescribeParameter(name = "srs", min = 0, description = "Target coordinate reference system (default is based on source when possible)") CoordinateReferenceSystem srs, + @DescribeParameter(name = "srsHandling", min = 0, description = "Desired SRS handling (default is FORCE_DECLARED, others are REPROJECT_TO_DECLARED or NONE)") ProjectionPolicy srsHandling, + @DescribeParameter(name = "styleName", min = 0, description = "Name of the style to be associated with the layer (default is a standard geometry-specific style)") String styleName) throws ProcessException { // first off, decide what is the target store diff --git a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/StoreCoverage.java b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/StoreCoverage.java index 0bd0a5ef7a5..00a3f1eb09d 100644 --- a/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/StoreCoverage.java +++ b/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/gs/StoreCoverage.java @@ -28,7 +28,7 @@ * @author Andrea Aime - GeoSolutions * @author ETj */ -@DescribeProcess(title = "storeCoverage", description = "Applies a raster symbolizer to the coverage") +@DescribeProcess(title = "Store Coverage", description = "Stores a raster on the server.") public class StoreCoverage implements GSProcess { private final static GeoTiffWriteParams DEFAULT_WRITE_PARAMS; @@ -49,9 +49,9 @@ public StoreCoverage(WPSStorageCleaner storage) { this.storage = storage; } - @DescribeResult(name = "coverageLocation", description = "The URL that can be used to retrieve the coverage") + @DescribeResult(name = "coverageLocation", description = "URL at which raster can be accessed") public URL execute( - @DescribeParameter(name = "coverage", description = "The raster to be styled") GridCoverage2D coverage) + @DescribeParameter(name = "coverage", description = "Input raster") GridCoverage2D coverage) throws IOException { final File file = File.createTempFile(coverage.getName().toString(), ".tif", storage.getStorage()); From 5e0e3dd626014b727413992d984d2c826bcc472b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 10 Aug 2012 13:20:51 -0700 Subject: [PATCH 34/72] GEOS-5244 - Added request parameter name to WPS Request Builder --- .../geoserver/wps/web/WPSRequestBuilder.java | 68 +++++++---- .../wps/web/WPSRequestBuilderPanel.java | 113 +++++++++++------- .../wps/web/WPSRequestBuilderTest.java | 29 +++++ 3 files changed, 145 insertions(+), 65 deletions(-) diff --git a/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilder.java b/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilder.java index 13f10f7bb8a..acab2ff1298 100644 --- a/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilder.java +++ b/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilder.java @@ -12,6 +12,7 @@ import javax.xml.transform.TransformerException; import org.apache.wicket.Page; +import org.apache.wicket.PageParameters; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; @@ -27,35 +28,54 @@ /** * Small embedded WPS client enabling users to visually build a WPS Execute request (and as a side - * effect also showing what capabilities and describe process would provide) + * effect also showing what capabilities and describe process would provide). + * + * Parameters: + *
      + *
    • name=procName - display the page showing given process + *
    * * @author Andrea Aime - OpenGeo + * @author Martin Davis - OpenGeo */ -@SuppressWarnings("serial") +@SuppressWarnings({"serial", "rawtypes"}) public class WPSRequestBuilder extends GeoServerBasePage { - ModalWindow responseWindow; - WPSRequestBuilderPanel builder; + public static final String PARAM_NAME = "name"; - public WPSRequestBuilder() { - // the form - Form form = new Form("form"); - add(form); - - // the actual request builder component - builder = new WPSRequestBuilderPanel("requestBuilder", new ExecuteRequest()); - form.add(builder); - - // the xml popup window - final ModalWindow xmlWindow = new ModalWindow("xmlWindow"); - add(xmlWindow); - xmlWindow.setPageCreator(new ModalWindow.PageCreator() { - - public Page createPage() { - return new PlainCodePage(xmlWindow, responseWindow, - getRequestXML()); - } - }); + ModalWindow responseWindow; + WPSRequestBuilderPanel builder; + + public WPSRequestBuilder(PageParameters parameters) { + this(parameters.getString(PARAM_NAME)); + } + + public WPSRequestBuilder() { + this((String) null); + } + + public WPSRequestBuilder(String procName) + { + // the form + Form form = new Form("form"); + add(form); + + // the actual request builder component + ExecuteRequest execRequest = new ExecuteRequest(); + if (procName != null) execRequest.processName = procName; + + builder = new WPSRequestBuilderPanel("requestBuilder", execRequest); + form.add(builder); + + // the xml popup window + final ModalWindow xmlWindow = new ModalWindow("xmlWindow"); + add(xmlWindow); + xmlWindow.setPageCreator(new ModalWindow.PageCreator() { + + public Page createPage() { + return new PlainCodePage(xmlWindow, responseWindow, getRequestXML()); + } + }); // the output response window responseWindow = new ModalWindow("responseWindow"); @@ -65,6 +85,7 @@ public Page createPage() { responseWindow.setPageCreator(new ModalWindow.PageCreator() { + @SuppressWarnings("unchecked") public Page createPage() { DemoRequest request = new DemoRequest(null); HttpServletRequest http = ((WebRequest) WPSRequestBuilder.this.getRequest()) @@ -79,6 +100,7 @@ public Page createPage() { form.add(new AjaxSubmitLink("execute") { + @SuppressWarnings("unchecked") @Override protected void onSubmit(AjaxRequestTarget target, Form form) { responseWindow.setDefaultModel(new Model(getRequestXML())); diff --git a/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilderPanel.java b/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilderPanel.java index 0a39883d065..6eea2e533ae 100644 --- a/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilderPanel.java +++ b/src/extension/wps/web-wps/src/main/java/org/geoserver/wps/web/WPSRequestBuilderPanel.java @@ -22,6 +22,7 @@ import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.DropDownChoice; @@ -39,6 +40,7 @@ import org.geoserver.ows.Ows11Util; import org.geoserver.ows.URLMangler.URLType; import org.geoserver.ows.util.ResponseUtils; +import org.geoserver.web.GeoServerBasePage; import org.geoserver.web.demo.DemoRequest; import org.geoserver.web.demo.DemoRequestResponse; import org.geoserver.web.wicket.CRSPanel; @@ -68,38 +70,52 @@ public class WPSRequestBuilderPanel extends Panel { private Component feedback; + private WebMarkupContainer descriptionContainer; + + private WebMarkupContainer inputContainer; + + private WebMarkupContainer outputContainer; + + private ListView inputView; + + private ListView outputView; + + /** + * Creates a panel to display a process and its parameters. + * Invoked with one of: + *
      + *
    • an empty executeRequest, which displays only the process dropdown + * + * + * @param id id of the panel + * @param executeRequest execute request, possibly with processName set + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) public WPSRequestBuilderPanel(String id, ExecuteRequest executeRequest) { super(id); setOutputMarkupId(true); this.execute = executeRequest; - boolean existingProcess = executeRequest.processName != null; - final DropDownChoice processChoice = new DropDownChoice("process", new PropertyModel( + final DropDownChoice processChoice = new DropDownChoice("process", new PropertyModel( execute, "processName"), buildProcessList()); add(processChoice); - // contains the process description and the describe process link - final WebMarkupContainer descriptionContainer = new WebMarkupContainer( + descriptionContainer = new WebMarkupContainer( "descriptionContainer"); - descriptionContainer.setVisible(existingProcess); + descriptionContainer.setVisible(false); add(descriptionContainer); // the process description final Label descriptionLabel = new Label("processDescription", new PropertyModel(this, "description")); descriptionContainer.add(descriptionLabel); - // if we're editing an existing process show the description - if (existingProcess) { - Name name = Ows11Util.name(execute.processName); - ProcessFactory pf = Processors.createProcessFactory(name); - description = pf.getDescription(name).toString(Locale.ENGLISH); - } - - // the process inputs - final WebMarkupContainer inputContainer = new WebMarkupContainer("inputContainer"); - inputContainer.setVisible(existingProcess); + // description value is set later in initProcessView() + + inputContainer = new WebMarkupContainer("inputContainer"); + inputContainer.setVisible(false); add(inputContainer); - final ListView inputView = new ListView("inputs", new PropertyModel(execute, "inputs")) { + inputView = new ListView("inputs", new PropertyModel(execute, "inputs")) { @Override protected void populateItem(ListItem item) { @@ -128,7 +144,7 @@ protected void populateItem(ListItem item) { Fragment f = new Fragment("paramValue", "literal", WPSRequestBuilderPanel.this); FormComponent literal = new TextField("literalValue", property); literal.setRequired(p.minOccurs > 0); - literal.setLabel(new Model(p.key)); + literal.setLabel(new Model(p.key)); f.add(literal); item.add(f); } @@ -137,17 +153,16 @@ protected void populateItem(ListItem item) { inputView.setReuseItems(true); inputContainer.add(inputView); - // the process outputs - final WebMarkupContainer outputContainer = new WebMarkupContainer("outputContainer"); - outputContainer.setVisible(existingProcess); + outputContainer = new WebMarkupContainer("outputContainer"); + outputContainer.setVisible(false); add(outputContainer); - final ListView outputView = new ListView("outputs", new PropertyModel(execute, "outputs")) { + outputView = new ListView("outputs", new PropertyModel(execute, "outputs")) { @Override protected void populateItem(ListItem item) { OutputParameter pv = (OutputParameter) item.getModelObject(); Parameter p = pv.getParameter(); - item.add(new CheckBox("include", new PropertyModel(pv, "include"))); + item.add(new CheckBox("include", new PropertyModel(pv, "include"))); item.add(new Label("param", buildParamSpec(p))); item.add(new Label("paramDescription", p.description.toString(Locale.ENGLISH))); if (pv.isComplex()) { @@ -178,7 +193,7 @@ public Page createPage() { .singletonMap("strict", "true"), URLType.SERVICE); request.setRequestUrl(url); request.setRequestBody((String) responseWindow.getDefaultModelObject()); - return new DemoRequestResponse(new Model(request)); + return new DemoRequestResponse(new Model(request)); } }); @@ -189,7 +204,7 @@ public Page createPage() { protected void onClick(AjaxRequestTarget target, Form form) { processChoice.processInput(); if (execute.processName != null) { - responseWindow.setDefaultModel(new Model(getDescribeXML(execute.processName))); + responseWindow.setDefaultModel(new Model(getDescribeXML(execute.processName))); responseWindow.show(target); } } @@ -206,27 +221,40 @@ protected void onClick(AjaxRequestTarget target, Form form) { @Override protected void onUpdate(AjaxRequestTarget target) { - Name name = Ows11Util.name(execute.processName); - ProcessFactory pf = Processors.createProcessFactory(name); - if (pf == null) { - error("No such process: " + execute.processName); - descriptionContainer.setVisible(false); - inputContainer.setVisible(false); - outputContainer.setVisible(false); - } else { - description = pf.getDescription(name).toString(Locale.ENGLISH); - execute.inputs = buildInputParameters(pf, name); - execute.outputs = buildOutputParameters(pf, name); - inputView.removeAll(); - outputView.removeAll(); - descriptionContainer.setVisible(true); - inputContainer.setVisible(true); - outputContainer.setVisible(true); - } + initProcessView(); target.addComponent(WPSRequestBuilderPanel.this); + + // ensure the parent page feedback panel gets refreshed to clear any existing err msg + // check for GeoServerBasePage, because parent page can also be a SubProcessBuilder + WebPage page = getWebPage(); + if (page instanceof GeoServerBasePage) { + target.addComponent(((GeoServerBasePage) page).getFeedbackPanel()); + } } }); + // handle process name submitted as request param + if (execute.processName != null) + initProcessView(); + } + private void initProcessView() { + Name name = Ows11Util.name(execute.processName); + ProcessFactory pf = Processors.createProcessFactory(name); + if (pf == null) { + error("No such process: " + execute.processName); + descriptionContainer.setVisible(false); + inputContainer.setVisible(false); + outputContainer.setVisible(false); + } else { + description = pf.getDescription(name).toString(Locale.ENGLISH); + execute.inputs = buildInputParameters(pf, name); + execute.outputs = buildOutputParameters(pf, name); + inputView.removeAll(); + outputView.removeAll(); + descriptionContainer.setVisible(true); + inputContainer.setVisible(true); + outputContainer.setVisible(true); + } } protected String getDescribeXML(String processId) { @@ -239,7 +267,7 @@ protected String getDescribeXML(String processId) { + " " + processId + "\n" + ""; } - String buildParamSpec(Parameter p) { + String buildParamSpec(Parameter p) { String spec = p.key; if (p.minOccurs > 0) { spec += "*"; @@ -314,4 +342,5 @@ public Component getFeedbackPanel() { return feedback; } + } diff --git a/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java b/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java index 8f84f4b4a62..0562e815228 100644 --- a/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java +++ b/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java @@ -2,6 +2,7 @@ import java.util.List; +import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.TextArea; @@ -9,6 +10,11 @@ import org.geoserver.web.GeoServerWicketTestSupport; import org.geoserver.wps.web.WPSRequestBuilder; +/** + * + * @author Martin Davis OpenGeo + * + */ public class WPSRequestBuilderTest extends GeoServerWicketTestSupport { public void testJTSAreaWorkflow() throws Exception { @@ -68,4 +74,27 @@ public void testJTSAreaWorkflow() throws Exception { // contents, // as that requires a true browser to execute the request } + + /** + * Tests initializing page to specific process via name request parameter. + * + * @throws Exception + */ + public void testNameRequest() throws Exception { + login(); + + // start the page + tester.startPage(new WPSRequestBuilder(new PageParameters("name=JTS:area"))); + + tester.assertComponent("form:requestBuilder:process", DropDownChoice.class); + + // check process description + tester.assertModelValue("form:requestBuilder:process", "JTS:area"); + + tester.assertComponent( + "form:requestBuilder:inputContainer:inputs:0:paramValue:editor:textarea", + TextArea.class); + } + + } From 0b1f5dd6ec5ad0689cff2f8ec8d9d11fc9cf47ec Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 15 Aug 2012 10:03:43 -0700 Subject: [PATCH 35/72] GEOS-5272 - Display line numbers in SLD validation error messages --- .../org/geoserver/wms/web/data/AbstractStylePage.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/web/wms/src/main/java/org/geoserver/wms/web/data/AbstractStylePage.java b/src/web/wms/src/main/java/org/geoserver/wms/web/data/AbstractStylePage.java index 7f5e36a97f2..98e1c7e1e4a 100644 --- a/src/web/wms/src/main/java/org/geoserver/wms/web/data/AbstractStylePage.java +++ b/src/web/wms/src/main/java/org/geoserver/wms/web/data/AbstractStylePage.java @@ -67,6 +67,7 @@ import org.geotools.styling.StyledLayerDescriptor; import org.geotools.styling.visitor.DuplicatingStyleVisitor; import org.opengis.metadata.citation.OnLineResource; +import org.xml.sax.SAXParseException; /** * Base page for creating/editing styles @@ -219,7 +220,7 @@ protected void onClick(AjaxRequestTarget target, Form form) { form.info( "No validation errors."); } else { for( Exception e : errors ) { - form.error( e ); + form.error( sldErrorWithLineNo(e) ); } } } @@ -231,6 +232,14 @@ protected IAjaxCallDecorator getAjaxCallDecorator() { }; } + private static String sldErrorWithLineNo(Exception e) { + if (e instanceof SAXParseException) { + SAXParseException se = (SAXParseException) e; + return "line " + se.getLineNumber() + ": " + e.getLocalizedMessage(); + } + return e.getLocalizedMessage(); + } + List validateSLD() { try { final String sld = editor.getInput(); From 1465a8f526cfaf81cd6f44e4bf8411a78960c3fb Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 10 Aug 2012 14:19:48 -0700 Subject: [PATCH 36/72] Fix unit test to match changed JTS:area process metadata --- .../test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java b/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java index 0562e815228..6509eca3bca 100644 --- a/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java +++ b/src/extension/wps/web-wps/src/test/java/org/geoserver/wps/web/WPSRequestBuilderTest.java @@ -48,7 +48,7 @@ public void testJTSAreaWorkflow() throws Exception { tester.assertModelValue("form:requestBuilder:process", "JTS:area"); Label label = (Label) tester .getComponentFromLastRenderedPage("form:requestBuilder:descriptionContainer:processDescription"); - assertTrue(label.getDefaultModelObjectAsString().contains("geometry area")); + assertTrue(label.getDefaultModelObjectAsString().contains("area")); tester.assertComponent( "form:requestBuilder:inputContainer:inputs:0:paramValue:editor:mime", From a2203f1e91eb1f0cfec718c9a6fe7c8e87d4fd5c Mon Sep 17 00:00:00 2001 From: Ben Caradoc-Davies Date: Tue, 14 Aug 2012 13:35:14 +0800 Subject: [PATCH 37/72] Fix build failure caused by GEOT-4224 process identifier changes --- .../java/org/geoserver/wps/ExecuteTest.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/extension/wps/wps-core/src/test/java/org/geoserver/wps/ExecuteTest.java b/src/extension/wps/wps-core/src/test/java/org/geoserver/wps/ExecuteTest.java index 1dc7b6e4ea8..2a592327e5c 100644 --- a/src/extension/wps/wps-core/src/test/java/org/geoserver/wps/ExecuteTest.java +++ b/src/extension/wps/wps-core/src/test/java/org/geoserver/wps/ExecuteTest.java @@ -204,7 +204,7 @@ public void testFeatureCollectionInline() throws Exception { // Standard Test A. "gs:BufferFeatureCollection" + "" + "" + - "feature collection" + + "features" + "" + "" + readFileIntoString("states-FeatureCollection.xml") + @@ -212,13 +212,13 @@ public void testFeatureCollectionInline() throws Exception { // Standard Test A. "" + "" + "" + - "width of the buffer" + + "distance" + "" + "10" + "" + "" + "" + - "name of the layer attribute containing the width of the buffer" + + "attributeName" + "" + "" + "" + @@ -250,7 +250,7 @@ public void testFeatureCollectionInlineBoundedBy() throws Exception { // Standar "gs:BufferFeatureCollection" + "" + "" + - "feature collection" + + "features" + "" + "" + readFileIntoString("restricted-FeatureCollection.xml") + @@ -258,7 +258,7 @@ public void testFeatureCollectionInlineBoundedBy() throws Exception { // Standar "" + "" + "" + - "width of the buffer" + + "distance" + "" + "1000" + "" + @@ -287,7 +287,7 @@ public void testFeatureCollectionInlineBoundedBy() throws Exception { // Standar public void testFeatureCollectionInlineKVP() throws Exception { String request = "wps?service=WPS&version=1.0.0&request=Execute&Identifier=gs:BufferFeatureCollection" + - "&DataInputs=" + urlEncode("feature collection=" + readFileIntoString("states-FeatureCollection.xml") + "@mimetype=application/wfs-collection-1.1;width of the buffer=10") + + "&DataInputs=" + urlEncode("features=" + readFileIntoString("states-FeatureCollection.xml") + "@mimetype=application/wfs-collection-1.1;distance=10") + "&ResponseDocument=" + urlEncode("result"); Document d = getAsDOM(request); @@ -307,7 +307,7 @@ public void testReferenceOutputXML() throws Exception { // Standard Test A.4.4.2 "gs:BufferFeatureCollection" + "" + "" + - "feature collection" + + "features" + "" + "" + readFileIntoString("restricted-FeatureCollection.xml") + @@ -315,7 +315,7 @@ public void testReferenceOutputXML() throws Exception { // Standard Test A.4.4.2 "" + "" + "" + - "width of the buffer" + + "distance" + "" + "1000" + "" + @@ -346,8 +346,8 @@ public void testReferenceOutputXML() throws Exception { // Standard Test A.4.4.2 public void testReferenceOutputKVP() throws Exception { String request = "wps?service=WPS&version=1.0.0&request=Execute&Identifier=gs:BufferFeatureCollection" + - "&DataInputs=" + urlEncode("feature collection=" + readFileIntoString("states-FeatureCollection.xml") + - "@mimetype=application/wfs-collection-1.1;width of the buffer=10") + + "&DataInputs=" + urlEncode("features=" + readFileIntoString("states-FeatureCollection.xml") + + "@mimetype=application/wfs-collection-1.1;distance=10") + "&ResponseDocument=" + urlEncode("result=@asReference=true"); Document d = getAsDOM(request); @@ -374,12 +374,12 @@ public void testFeatureCollectionFileReference() throws Exception { // Standard "gs:BufferFeatureCollection" + "" + "" + - "feature collection" + + "features" + " \n" + "" + "" + - "width of the buffer" + + "distance" + "" + "10" + "" + @@ -408,8 +408,8 @@ public void testFeatureCollectionFileReference() throws Exception { // Standard public void testFeatureCollectionFileReferenceKVP() throws Exception { URL collectionURL = getClass().getResource("states-FeatureCollection.xml"); String request = "wps?service=WPS&version=1.0.0&request=Execute&Identifier=gs:BufferFeatureCollection" + - "&DataInputs=" + urlEncode("feature collection=@mimetype=application/wfs-collection-1.1@xlink:href=" - + collectionURL.toExternalForm() + ";width of the buffer=10") + "&ResponseDocument=" + urlEncode("result"); + "&DataInputs=" + urlEncode("features=@mimetype=application/wfs-collection-1.1@xlink:href=" + + collectionURL.toExternalForm() + ";distance=10") + "&ResponseDocument=" + urlEncode("result"); Document d = getAsDOM(request); // print(d); @@ -428,7 +428,7 @@ public void testInlineGeoJSON() throws Exception { "gs:BufferFeatureCollection" + "" + "" + - "feature collection" + + "features" + "" + "" + "" + "" + - "width of the buffer" + + "distance" + "" + "10" + "" + @@ -464,7 +464,7 @@ public void testShapeZip() throws Exception { "gs:BufferFeatureCollection" + "" + " \n" + - "feature collection" + + "features" + "" + "" + readFileIntoString("states-FeatureCollection.xml") + @@ -472,7 +472,7 @@ public void testShapeZip() throws Exception { "" + "" + "" + - "width of the buffer" + + "distance" + "" + "10" + "" + From fd0708ac4ba45846972976150da8789514f000fe Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Wed, 22 Aug 2012 12:29:26 +0200 Subject: [PATCH 38/72] [GEOS-2801] WCS Reprojection using an XML GetCoverage request seems to be somewhat broken (WCS 1.1.1) --- .../java/org/geoserver/data/test/rain.zip | Bin 0 -> 5447 bytes .../org/vfny/geoserver/util/WCSUtils.java | 34 ++++- .../wcs/DefaultWebCoverageService111.java | 69 ++++++---- .../wcs/AbstractGetCoverageTest.java | 10 -- .../org/geoserver/wcs/GetCoverageTest.java | 130 +++++++++++------- 5 files changed, 159 insertions(+), 84 deletions(-) create mode 100644 src/main/src/test/java/org/geoserver/data/test/rain.zip diff --git a/src/main/src/test/java/org/geoserver/data/test/rain.zip b/src/main/src/test/java/org/geoserver/data/test/rain.zip new file mode 100644 index 0000000000000000000000000000000000000000..adf566632b89fe502ba95d6c6d7c9ae8a7d5423f GIT binary patch literal 5447 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 zJvf%0=Interpolating
      diff --git a/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java b/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java index 0f9f9e2ea82..6a656e0d2b8 100644 --- a/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java +++ b/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java @@ -159,9 +159,8 @@ public GridCoverage[] getCoverage(GetCoverageType request) { final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); final BoundingBoxType bbox = request.getDomainSubset().getBoundingBox(); final CoordinateReferenceSystem nativeCRS = originalEnvelope.getCoordinateReferenceSystem(); - final GeneralEnvelope requestedEnvelopeInSourceCRS; + final GeneralEnvelope requestedEnvelopeInNativeCRS; final GeneralEnvelope requestedEnvelope; - MathTransform bboxToNativeTx=null; if (bbox != null) { // first off, parse the envelope corners double[] lowerCorner = new double[bbox.getLowerCorner().size()]; @@ -175,33 +174,33 @@ public GridCoverage[] getCoverage(GetCoverageType request) { // if no crs has beens specified, the native one is assumed if (bbox.getCrs() == null) { requestedEnvelope.setCoordinateReferenceSystem(nativeCRS); - requestedEnvelopeInSourceCRS = requestedEnvelope; + requestedEnvelopeInNativeCRS = requestedEnvelope; } else { // otherwise we need to transform final CoordinateReferenceSystem bboxCRS = CRS.decode(bbox.getCrs()); requestedEnvelope.setCoordinateReferenceSystem(bboxCRS); - bboxToNativeTx = CRS.findMathTransform(bboxCRS, nativeCRS,true); - if(!bboxToNativeTx.isIdentity()){ - requestedEnvelopeInSourceCRS = CRS.transform(bboxToNativeTx,requestedEnvelope); - requestedEnvelopeInSourceCRS.setCoordinateReferenceSystem(nativeCRS); + if(!CRS.equalsIgnoreMetadata(bboxCRS, nativeCRS)) { + CoordinateOperationFactory of = CRS.getCoordinateOperationFactory(true); + CoordinateOperation co = of.createOperation(bboxCRS, nativeCRS); + requestedEnvelopeInNativeCRS = CRS.transform(co, requestedEnvelope); + } else { + requestedEnvelopeInNativeCRS= new GeneralEnvelope(requestedEnvelope); } - else - requestedEnvelopeInSourceCRS= new GeneralEnvelope(requestedEnvelope); } } else { - requestedEnvelopeInSourceCRS = reader.getOriginalEnvelope(); - requestedEnvelope = requestedEnvelopeInSourceCRS; + requestedEnvelopeInNativeCRS = reader.getOriginalEnvelope(); + requestedEnvelope = requestedEnvelopeInNativeCRS; } final GridCrsType gridCRS = request.getOutput().getGridCRS(); - // Compute the target crs, the crs that the final coverage will be - // served into + // Compute the crs that the final coverage will be served into final CoordinateReferenceSystem targetCRS; - if (gridCRS == null) + if (gridCRS == null) { targetCRS = reader.getOriginalEnvelope().getCoordinateReferenceSystem(); - else + } else { targetCRS = CRS.decode(gridCRS.getGridBaseCRS()); + } // // Raster destination size @@ -211,6 +210,8 @@ public GridCoverage[] getCoverage(GetCoverageType request) { // grab the grid to world transformation MathTransform gridToCRS = reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER); + double pixelSizeX = 1; + double pixelSizeY = 1; if (gridCRS != null) { Double[] origin = (Double[]) gridCRS.getGridOrigin(); Double[] offsets = (Double[]) gridCRS.getGridOffsets(); @@ -289,6 +290,8 @@ public GridCoverage[] getCoverage(GetCoverageType request) { } } } + pixelSizeX = Math.abs(tx.getScaleX()); + pixelSizeY = Math.abs(tx.getScaleY()); gridToCRS = new AffineTransform2D(tx); } @@ -319,13 +322,8 @@ public GridCoverage[] getCoverage(GetCoverageType request) { // now we have enough info to read the coverage, grab the parameters // and add the grid geometry info - final GeneralEnvelope intersectionEnvelopeInSourceCRS = new GeneralEnvelope(requestedEnvelopeInSourceCRS); + final GeneralEnvelope intersectionEnvelopeInSourceCRS = new GeneralEnvelope(requestedEnvelopeInNativeCRS); intersectionEnvelopeInSourceCRS.intersect(originalEnvelope); - final GeneralEnvelope intersectionEnvelope= - bboxToNativeTx.isIdentity()? - new GeneralEnvelope(intersectionEnvelopeInSourceCRS): - CRS.transform(bboxToNativeTx.inverse(), intersectionEnvelopeInSourceCRS); - intersectionEnvelope.setCoordinateReferenceSystem(targetCRS); final GridGeometry2D requestedGridGeometry = new GridGeometry2D(PixelInCell.CELL_CORNER, gridToCRS, intersectionEnvelopeInSourceCRS, null); @@ -416,6 +414,11 @@ public GridCoverage[] getCoverage(GetCoverageType request) { // now that we have read the coverage double check the input size WCSUtils.checkInputLimits(wcs, coverage); + + // some raster sources do not really read less data (arcgrid for example), we may need to crop + if(!intersectionEnvelopeInSourceCRS.contains(coverage.getEnvelope2D(), true)) { + coverage = WCSUtils.crop(coverage, intersectionEnvelopeInSourceCRS); + } /** * Band Select (works on just one field) @@ -487,14 +490,34 @@ public GridCoverage[] getCoverage(GetCoverageType request) { * Reproject */ // adjust the grid geometry to use the final bbox and crs - final GridGeometry2D destinationGridGeometry = new GridGeometry2D(PixelInCell.CELL_CORNER, gridToCRS, intersectionEnvelope, null); + final GeneralEnvelope intersectionEnvelope; + boolean reprojectionNeeded = !CRS.equalsIgnoreMetadata(nativeCRS, targetCRS); + if(reprojectionNeeded) { + CoordinateOperationFactory of = CRS.getCoordinateOperationFactory(true); + CoordinateOperation co = of.createOperation(nativeCRS, targetCRS); + intersectionEnvelope = CRS.transform(co, intersectionEnvelopeInSourceCRS); + } else { + intersectionEnvelope = intersectionEnvelopeInSourceCRS; + } + // adjust to have at least one pixel in the output + if(intersectionEnvelope.getSpan(0) < Math.abs(pixelSizeX)) { + double minX = intersectionEnvelope.getMinimum(0); + intersectionEnvelope.setRange(0, minX, minX + pixelSizeX); + } + if(intersectionEnvelope.getSpan(1) < Math.abs(pixelSizeY)) { + double minY = intersectionEnvelope.getMinimum(1); + intersectionEnvelope.setRange(1, minY, minY + pixelSizeY); + } + + final GridGeometry2D destinationGridGeometry = new GridGeometry2D(PixelInCell.CELL_CENTER, gridToCRS, intersectionEnvelope, null); // before extracting the output make sure it's not too big WCSUtils.checkOutputLimits(wcs, destinationGridGeometry.getGridRange2D(), bandSelectedCoverage.getRenderedImage().getSampleModel()); // reproject if necessary - if(!CRS.equalsIgnoreMetadata(nativeCRS, targetCRS) || !bandSelectedCoverage.getGridGeometry().equals(destinationGridGeometry)) { + boolean sameGridGeometry = bandSelectedCoverage.getGridGeometry().equals(destinationGridGeometry); + if(reprojectionNeeded || !sameGridGeometry) { final GridCoverage2D reprojectedCoverage = WCSUtils.resample( bandSelectedCoverage, nativeCRS, targetCRS, destinationGridGeometry,interpolation); diff --git a/src/wcs1_1/src/test/java/org/geoserver/wcs/AbstractGetCoverageTest.java b/src/wcs1_1/src/test/java/org/geoserver/wcs/AbstractGetCoverageTest.java index 955d243100b..95c56dd236a 100644 --- a/src/wcs1_1/src/test/java/org/geoserver/wcs/AbstractGetCoverageTest.java +++ b/src/wcs1_1/src/test/java/org/geoserver/wcs/AbstractGetCoverageTest.java @@ -1,15 +1,9 @@ package org.geoserver.wcs; -import static org.geoserver.data.test.MockData.*; -import static org.vfny.geoserver.wcs.WcsException.WcsExceptionCode.*; - import java.io.StringReader; import java.util.HashMap; import java.util.Map; -import javax.servlet.ServletResponse; - -import junit.framework.Test; import net.opengis.wcs11.GetCoverageType; import org.geoserver.config.GeoServer; @@ -17,11 +11,7 @@ import org.geoserver.wcs.test.WCSTestSupport; import org.geoserver.wcs.xml.v1_1_1.WCSConfiguration; import org.geoserver.wcs.xml.v1_1_1.WcsXmlReader; -import org.geotools.coverage.grid.GridCoverage2D; -import org.geotools.referencing.CRS; import org.opengis.coverage.grid.GridCoverage; -import org.vfny.geoserver.wcs.WcsException; -import org.w3c.dom.Document; public abstract class AbstractGetCoverageTest extends WCSTestSupport { diff --git a/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java b/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java index 555c1593caf..362fb161aa5 100644 --- a/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java +++ b/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java @@ -1,12 +1,12 @@ package org.geoserver.wcs; -import static org.geoserver.data.test.MockData.TASMANIA_BM; -import static org.geoserver.data.test.MockData.WORLD; -import static org.vfny.geoserver.wcs.WcsException.WcsExceptionCode.InvalidParameterValue; +import static org.geoserver.data.test.MockData.*; +import static org.vfny.geoserver.wcs.WcsException.WcsExceptionCode.*; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileOutputStream; +import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; @@ -19,14 +19,19 @@ import junit.framework.Test; import org.apache.commons.io.IOUtils; +import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CoverageInfo; import org.geoserver.data.test.MockData; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.gce.geotiff.GeoTiffReader; import org.geotools.geometry.Envelope2D; +import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; +import org.geotools.referencing.operation.transform.AffineTransform2D; import org.opengis.coverage.grid.GridCoverage; import org.opengis.coverage.grid.GridEnvelope; +import org.opengis.geometry.Envelope; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.vfny.geoserver.wcs.WcsException; import org.w3c.dom.Document; @@ -35,6 +40,7 @@ public class GetCoverageTest extends AbstractGetCoverageTest { private static final QName MOSAIC = new QName(MockData.SF_URI, "rasterFilter", MockData.SF_PREFIX); + private static final QName RAIN = new QName(MockData.SF_URI, "rain", MockData.SF_PREFIX); /** @@ -57,36 +63,12 @@ protected void populateDataDirectory(MockData dataDirectory) throws Exception { // this also adds the raster style dataDirectory.addCoverage(MOSAIC, MockData.class.getResource("raster-filter-test.zip"), null, "raster"); + + // add a data source that cannot restrict spatial data on its own + dataDirectory.addCoverageFromZip(RAIN, + MockData.class.getResource("rain.zip"), null, "raster"); } - // public void testNullGridOrigin() throws Exception { - // String request = "\r\n" + // - // "\r\n" + // - // " wcs:BlueMarble\r\n" + // - // " \r\n" + // - // " \r\n" + // - // " -90 -180\r\n" + // - // " 90 180\r\n" + // - // " \r\n" + // - // " \r\n" + // - // " \r\n" + // - // " \r\n" + // - // " urn:ogc:def:crs:EPSG:6.6:4326\r\n" + // - // " urn:ogc:def:method:WCS:1.1:2dSimpleGrid\r\n" + // - // " -1 2\r\n" + // - // " \r\n" + // - // " \r\n" + // - // ""; - // - // executeGetCoverageXml(request); - // } - public void testKvpBasic() throws Exception { Map raw = baseMap(); final String getLayerId = getLayerId(TASMANIA_BM); @@ -164,18 +146,36 @@ public void testWrongFormatParams() throws Exception { } } - // public void testDefaultGridOrigin() throws Exception { - // Map raw = new HashMap(); - // final String getLayerId = getLayerId(TASMANIA_BM); - // raw.put("identifier", getLayerId); - // raw.put("format", "image/geotiff"); - // raw.put("BoundingBox", "-45,146,-42,147,urn:ogc:def:crs:EPSG:6.6:4326"); - // - // GridCoverage[] coverages = executeGetCoverageKvp(raw); - // AffineTransform2D tx = (AffineTransform2D) coverages[0].getGridGeometry().getGridToCRS(); - // assertEquals(0.0, tx.getTranslateX()); - // assertEquals(0.0, tx.getTranslateY()); - // } + public void testDefaultGridOrigin() throws Exception { + Map raw = new HashMap(baseMap()); + final String getLayerId = getLayerId(TASMANIA_BM); + raw.put("identifier", getLayerId); + raw.put("format", "image/geotiff"); + // use a bbox larger than the source + raw.put("BoundingBox", "-45,146,-42,149,urn:ogc:def:crs:EPSG:6.6:4326"); + + GridCoverage[] coverages = executeGetCoverageKvp(raw); + AffineTransform2D tx = (AffineTransform2D) coverages[0].getGridGeometry().getGridToCRS(); + // take into account the "pixel is area" convention + assertEquals(0.0, tx.getTranslateX() + tx.getScaleX() / 2, 1e-9); + assertEquals(0.0, tx.getTranslateY() + tx.getScaleY() / 2, 1e-9); + } + + public void testSpatialSubsetOnePixel() throws Exception { + Map raw = new HashMap(baseMap()); + final String getLayerId = getLayerId(RAIN); + raw.put("identifier", getLayerId); + raw.put("format", "image/geotiff"); + // this bbox is inside, and smaller than a single pixel + raw.put("BoundingBox", "-45,146,-42,149,urn:ogc:def:crs:EPSG:6.6:4326"); + + GridCoverage[] coverages = executeGetCoverageKvp(raw); + Envelope envelope = coverages[0].getEnvelope(); + assertEquals(-45d, envelope.getMinimum(0)); + assertEquals(-40d, envelope.getMaximum(0)); + assertEquals(146d, envelope.getMinimum(1)); + assertEquals(151d, envelope.getMaximum(1)); + } public void testWrongGridOrigin() throws Exception { Map raw = baseMap(); @@ -192,6 +192,36 @@ public void testWrongGridOrigin() throws Exception { assertEquals("GridOrigin", e.getLocator()); } } + + public void testReproject() throws Exception { + // add the target code to the supported ones + Catalog catalog = getCatalog(); + final String layerId = getLayerId(TASMANIA_BM); + CoverageInfo ci = catalog.getCoverageByName(layerId); + ci.getResponseSRS().add("EPSG:3857"); + catalog.save(ci); + + // do the request + Map raw = baseMap(); + raw.put("identifier", layerId); + raw.put("format", "image/geotiff"); + raw.put("BoundingBox", "-80,-180,80,180,urn:ogc:def:crs:EPSG:6.6:4326"); + raw.put("GridBaseCRS", "EPSG:3857"); + GridCoverage[] coverages = executeGetCoverageKvp(raw); + + Envelope envelope = coverages[0].getEnvelope(); + System.out.println(envelope); + CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:3857"); + assertEquals(targetCRS, envelope.getCoordinateReferenceSystem()); + + ReferencedEnvelope nativeBounds = ci.getNativeBoundingBox(); + ReferencedEnvelope expected = nativeBounds.transform(targetCRS, true); + + assertEquals(expected.getMinimum(0), envelope.getMinimum(0)); + assertEquals(expected.getMaximum(0), envelope.getMaximum(0)); + assertEquals(expected.getMinimum(1), envelope.getMinimum(1)); + assertEquals(expected.getMaximum(1), envelope.getMaximum(1)); + } public void testWorkspaceQualified() throws Exception { String queryString = "&request=getcoverage&service=wcs&version=1.1.1&&format=image/geotiff" @@ -240,19 +270,19 @@ public void testLargerThanData() throws Exception { CoverageInfo ci = getCatalog().getCoverageByName(TASMANIA_BM.getLocalPart()); GridCoverage2D original = (GridCoverage2D) ci.getGridCoverage(null, null); - // the grid should be swapped, axis flipping... + // the grid should not be swapped since the target output is expressed in EPSG:XYWZ form GridEnvelope originalRange = original.getGridGeometry().getGridRange(); GridEnvelope actualRange = result.getGridGeometry().getGridRange(); - assertEquals(originalRange.getSpan(0), actualRange.getSpan(1)); - assertEquals(originalRange.getSpan(1), actualRange.getSpan(0)); + assertEquals(originalRange.getSpan(0), actualRange.getSpan(0)); + assertEquals(originalRange.getSpan(1), actualRange.getSpan(1)); // check also the geographic bounds Envelope2D originalEnv = original.getEnvelope2D(); Envelope2D actualEnv = result.getEnvelope2D(); - assertEquals(originalEnv.getMinX(), actualEnv.getMinY(), 1e-6); - assertEquals(originalEnv.getMinY(), actualEnv.getMinX(), 1e-6); - assertEquals(originalEnv.getMaxX(), actualEnv.getMaxY(), 1e-6); - assertEquals(originalEnv.getMaxY(), actualEnv.getMaxX(), 1e-6); + assertEquals(originalEnv.getMinX(), actualEnv.getMinX(), 1e-6); + assertEquals(originalEnv.getMinY(), actualEnv.getMinY(), 1e-6); + assertEquals(originalEnv.getMaxX(), actualEnv.getMaxX(), 1e-6); + assertEquals(originalEnv.getMaxY(), actualEnv.getMaxY(), 1e-6); // cleanup tiffFile.delete(); From 06444ce253c16c9f4f84859190748035649bfeef Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Wed, 22 Aug 2012 14:12:53 +0200 Subject: [PATCH 39/72] fix the build --- src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java b/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java index 362fb161aa5..1eee5c4c825 100644 --- a/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java +++ b/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java @@ -65,7 +65,7 @@ protected void populateDataDirectory(MockData dataDirectory) throws Exception { MockData.class.getResource("raster-filter-test.zip"), null, "raster"); // add a data source that cannot restrict spatial data on its own - dataDirectory.addCoverageFromZip(RAIN, + dataDirectory.addCoverage(RAIN, MockData.class.getResource("rain.zip"), null, "raster"); } From a9a50cf51c6778eebc1fcad41db642b11688f3a3 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Thu, 23 Aug 2012 16:28:44 +0200 Subject: [PATCH 40/72] Fixing GEOS-5275 --- .../security/web/role/RolePaletteFormComponent.html | 3 ++- .../security/web/role/RolePaletteFormComponent.java | 8 ++++++++ .../src/main/resources/GeoServerApplication.properties | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.html b/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.html index 2bed84288e7..f8ed5e509dc 100644 --- a/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.html +++ b/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.html @@ -7,7 +7,8 @@
      • - roles + +
        • diff --git a/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.java b/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.java index 63313022c38..ff5c08f4e57 100644 --- a/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.java +++ b/src/web/security/src/main/java/org/geoserver/security/web/role/RolePaletteFormComponent.java @@ -8,9 +8,11 @@ import java.util.Collection; import java.util.List; +import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.SubmitLink; import org.apache.wicket.model.IModel; +import org.apache.wicket.model.StringResourceModel; import org.geoserver.security.GeoServerRoleService; import org.geoserver.security.GeoServerSecurityManager; import org.geoserver.security.impl.GeoServerRole; @@ -53,6 +55,12 @@ public RolePaletteFormComponent(String id, IModel> model, GeoServerRoleService roleService = getSecurityManager().getActiveRoleService(); final String roleServiceName = roleService.getName(); + if (choicesModel instanceof RuleRolesModel) + add(new Label("roles", new StringResourceModel("roles",this,null))); + else + add(new Label("roles", new StringResourceModel("rolesFromActiveService", + this,null,new Object[] {roleServiceName}))); + add(new SubmitLink("addRole") { @Override public void onSubmit() { diff --git a/src/web/security/src/main/resources/GeoServerApplication.properties b/src/web/security/src/main/resources/GeoServerApplication.properties index 8a1db10d62b..f42d5f52f99 100644 --- a/src/web/security/src/main/resources/GeoServerApplication.properties +++ b/src/web/security/src/main/resources/GeoServerApplication.properties @@ -228,6 +228,7 @@ RolePanel.th.hasroleparams=Parameters RolePanel.th.remove=Remove RolePaletteFormComponent.roles=Roles +RolePaletteFormComponent.rolesFromActiveService=Roles taken from active role service: {0} RolePaletteFormComponent.addRole=Add a new role RuleRolesFormComponent.anyRole=Grant access to any role From 872277e6135f1bbe225715de41a03a68a0d8f740 Mon Sep 17 00:00:00 2001 From: Matt Walker Date: Thu, 16 Aug 2012 13:54:17 +0100 Subject: [PATCH 41/72] Updated name of template referenced in the Freemarker tutorial. The template name used when describing the template lookup matches the actual name of the content template (content.ftl). Backport of pull request #17 from Matt Walker. --- doc/en/user/source/tutorials/freemarker.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/en/user/source/tutorials/freemarker.rst b/doc/en/user/source/tutorials/freemarker.rst index d49260f31ee..374a5e3fdd6 100644 --- a/doc/en/user/source/tutorials/freemarker.rst +++ b/doc/en/user/source/tutorials/freemarker.rst @@ -16,11 +16,11 @@ Most of the relevant information about how to approach template writing is inclu Template Lookup ``````````````` -Geoserver looks up templates in three different places, allowing you for various level of customization. Given a templated output, a template name (``template.ftl``) and a feature type (``myFeatureType``), Geoserver will perform the following lookups: +Geoserver looks up templates in three different places, allowing for various level of customization. For example given the ``content.ftl`` template used to generate WMS GetFeatureInfo content: -* Look into ``GEOSERVER_DATA_DIR/workspaces///myfeatureType/template.ftl`` to see if there is a type specific template -* Look into ``GEOSERVER_DATA_DIR/templates//template.ftl`` to see if there is a workspace-specific template -* Look into ``GEOSERVER_DATA_DIR/templates/template.ftl`` looking for a global override +* Look into ``GEOSERVER_DATA_DIR/workspaces////content.ftl`` to see if there is a feature type specific template +* Look into ``GEOSERVER_DATA_DIR/templates//content.ftl`` to see if there is a workspace specific template +* Look into ``GEOSERVER_DATA_DIR/templates/content.ftl`` looking for a global override * Look into the GeoServer classpath and load the default template Each templated output format tutorial should provide you with the template names, and state whether the templates can be type specific, or not. Missing the source for the default template, look up for the service jar in the geoserver distribution (for example, wms-x.y.z.jar), unpack it, and you'll find the actual xxx.ftl files GeoServer is using as the default templates. From 9a5e438e3214af41813fc785a5ab79ceedf8ed66 Mon Sep 17 00:00:00 2001 From: David Winslow Date: Wed, 15 Aug 2012 17:01:02 -0400 Subject: [PATCH 42/72] Support reduced accuracy in WMS TIME parameter This fixes GEOS-5280. --- doc/en/user/source/services/wms/index.rst | 1 + doc/en/user/source/services/wms/reference.rst | 7 + doc/en/user/source/services/wms/time.rst | 188 ++++++++++++++++ .../org/geoserver/ows/kvp/TimeKvpParser.java | 112 +++++----- .../geoserver/ows/kvp/TimeKvpParserTest.java | 208 +++++++++++++++--- .../wms/WMSDimensionsTestSupport.java | 2 +- .../wms/map/GetMapKvpRequestReaderTest.java | 8 +- 7 files changed, 423 insertions(+), 103 deletions(-) create mode 100644 doc/en/user/source/services/wms/time.rst diff --git a/doc/en/user/source/services/wms/index.rst b/doc/en/user/source/services/wms/index.rst index 81158874d09..50923b392d6 100644 --- a/doc/en/user/source/services/wms/index.rst +++ b/doc/en/user/source/services/wms/index.rst @@ -8,6 +8,7 @@ Web Map Service basics reference + time outputformats vendor configuration diff --git a/doc/en/user/source/services/wms/reference.rst b/doc/en/user/source/services/wms/reference.rst index b407cec649d..0a9ba171771 100644 --- a/doc/en/user/source/services/wms/reference.rst +++ b/doc/en/user/source/services/wms/reference.rst @@ -199,6 +199,13 @@ An example request for a PNG map image using default styling is: &height=330 &format=image%2Fpng +Time +.... + +As of GeoServer 2.2.0, GeoServer supports a TIME attribute for WMS GetMap requests as described in version 1.3 of the WMS specification. +This parameter allows filtering a dataset by temporal slices as well as spatial tiles for rendering. +See :doc:`/services/wms/time` for information on its use. + .. _wms_getfeatureinfo: diff --git a/doc/en/user/source/services/wms/time.rst b/doc/en/user/source/services/wms/time.rst new file mode 100644 index 00000000000..c59a41f7dea --- /dev/null +++ b/doc/en/user/source/services/wms/time.rst @@ -0,0 +1,188 @@ +Time Support in Geoserver WMS +============================= + +For layers that are properly configured with a TIME dimension, GeoServer supports a TIME attribute in GetMap requests to specify a temporal subset for rendering. +For example, you might have a single dataset with weather observations collected over time and choose to plot a single day's worth of observations. + +Specifying a Time +----------------- + +The format used for specifying a time in the WMS TIME parameter is based on `ISO-8601 `_. +Times may be specified to a precision of 1 millisecond; GeoServer does not represent time queries with more precision than this. +Times follow the general format:: + + yyyy-MM-ddThh:mm:ss.SSSZ + +That is, a day specified by a 4-digit year, 2-digit month, and 2-digit day-of-month field, and an instant on that day specified by 2-digit hour, minute, and second fields, with an arbitrary number of decimal digits after the seconds field. +The day and instant seconds are separated with a capital 'T', and the entire thing is suffixed with a 'Z' (indicating 'Zulu' or `UTC ` for the time zone. The WMS specification does not provide for other time zones.) + +GeoServer will apply the TIME value to all temporally enabled layers in the LAYERS parameter of the GetMap request. +Layers without a temporal component will be served normally - allowing clients to include reference information like political boundaries along with temporal data. + +Examples +........ + + * December 12, 2001 at 6:00 PM would be represented as:: + + TIME=2001-12-12T18:00:00.0Z + + * May 5, 1993 at 11:34 PM would be represented as:: + + TIME=1993-05-05T11:34:00.0Z + +Specifying a Periodicity +------------------------ + +The periodicity is also specified in ISO-8601 format: a capital P followed by one or more interval lengths, each consisting of a number and a letter identifying a time unit: + +.. list-table:: + + * - **Unit** + - **Abbreviation** + * - Years + - ``Y`` + * - Months + - ``M`` + * - Days + - ``D`` + * - Hours + - ``H`` + * - Minutes + - ``M`` + * - Seconds + - ``S`` + +The Year/Month/Day group of values must be separated from the Hours/Minutes/Seconds group by a ``T`` character. +Additionally, fields which contain a 0 may be omitted entirely, and the T may be omitted if hours, minutes, and seconds are all omitted. +Fractional values are permitted, but only for the most specific value that is included. + +The period must divide evenly into the interval defined by the start/end times. + +Examples of Periods +................... + + * One hour:: + + P0Y0M0DT1H0M0S + + PT1H0M0S + + PT1H + + * 90 minutes (equivalent to 1 hour, 30 minutes):: + + P0Y0M0DT1H30M0S + + PT1H30M + + P90M + + * 18 months:: + + P1Y6M0DT0H0M0S + + P1Y6M0D + + P0Y18M0DT0H0M0S + + P18M + + *But not* `` P1.25Y3M + +Specifying an Interval +---------------------- + +A client may request information over a continuous interval instead of a single instant by specifying a start and end time, separated by a ``/`` character. +In this scenario the start and end are both inclusive; that is, samples from exactly the endpoints of the specified range will be included in the rendered tile. + +Examples +........ + + +.. list-table:: + + * - Description + - Time specification + * - The month of September 2002 + - ``2002-09-01T00:00:00.0Z/2002-10-01T23:59:59.999Z`` + * - The entire day of December 25, 2010 + - ``2010-12-25T00:00:00.0Z/2010-12-25T23:59:59.999Z`` + +.. note:: + + Because the time interval is inclusive, we cannot precisely specify a concept such as "all times within day x". + We must choose between incorrectly accepting observations that occur at the end point, and incorrectly excluding some fraction of the final second of the interval. + In practice, GeoServer and many data storage engines have limited resolution in their representations, so approximating a range to the nearest millisecond is 'as good as we can do.' + It is possible that this technical constraint may be lifted at some point in the future. + +Reduced Accuracy Times +---------------------- + +The WMS specification also allows time specifications to be truncated, by omitting some suffix of the time string. +In this case, GeoServer treats the time as a range whose length is equal to one of the most precise unit specified in the time string. +If time specification omits all fields except year, it identifies a range one year long starting at the beginning of that year, etc. + +GeoServer implements this by adding the appropriate unit, then subtracting 1 millisecond. +This avoids surprising results when using an interval that aligns with the actual sampling frequency of the data - for example, if yearly data is natively stored with dates like 2001-01-01T00:00:00.0Z, 2002-01-01T00:00:00Z, etc. then a request for 2001 would include the samples for both 2001 and 2002 otherwise. + +Examples +........ + +.. list-table:: + + * - Description + - Reduced Accuracy Time + - Equivalent Range + * - The month of September 2002 + - ``2002-09`` + - ``2002-09-01T00:00:00.0Z/2002-10-01T23:59:59.999Z`` + * - The day of December 25, 2010:: + - ``2010-12-25`` + - ``2010-12-25T00:00:00.0Z/2010-12-25T23:59:59.999Z`` + +Ranges with Reduced Accuracy Times +---------------------------------- + +Reduced accuracy times are also allowed when specifying ranges. +In this case, GeoServer effectively expands the start and end times as described above, and then includes any samples from after the beginning of the start interval and before the end of the end interval. + +.. list-table:: + + * - Description + - Reduced Accuracy Time + - Equivalent Range + * - The months of September through December 2002 + - ``2002-09/2002-12`` + - ``2002-09-01T00:00:00.0Z/2002-12-31T23:59:59.999Z`` + * - 12pm through 6pm, December 25, 2010:: + - ``2010-12-25T12/2010-12-25T18`` + - ``2010-12-25T12:00:00.0Z/2010-12-25T18:59:59.999Z`` + +Specifying a List of Times +-------------------------- + +For some formats, GeoServer can generate an animation. +In this case, the client must specify multiple times, one for each frame. +When multiple times are needed, the client should simply format each time as described above, and separate them with commas. + +If the list is evenly spaced (for example, daily or hourly samples) then the list may be specified as a range, using a start time, end time, and period separated by slashes. + +Examples +........ + +.. list-table:: + + * - Description + - List notation + - Range notation + * - Noon every day for the week of August 12-18, 2012:: + - ``TIME=2012-08-12T12:00:00.0Z,2012-08-13T12:00:00.0Z,2012-08-14T12:00:00.0Z,2012-08-15T12:00:00.0Z,2012-08-16T12:00:00.0Z,2012-08-17T12:00:00.0Z,2012-08-18T12:00:00.0Z`` + - ``TIME=2012-08-12T12:00:00.0Z/2012-08-18:T12:00:00.0Z/P1D`` + * - Midnight on the first of September, October, and November 1999:: + - ``TIME=1999-09-01T00:00:00.0Z,1999-10-01T00:00:00.0Z,1999-11-01T00:00:00.0Z + - ``TIME=1999-09-01T00:00:00.0Z/1999-11-01T00:00:00.0Z/P1M`` + +.. note:: + + GeoServer currently does not support lists of ranges, so all list queries effectively have a resolution of 1 millisecond. + If you use reduced accuracy notation when specifying a range, each range will be automatically converted to the instant at the beginning of the range. diff --git a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java index f1e70200f2f..8020fb1df73 100644 --- a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java +++ b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -21,6 +22,8 @@ import java.util.TimeZone; import java.util.TreeSet; import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.geoserver.ows.KvpParser; import org.geoserver.platform.GeoServerExtensions; @@ -36,20 +39,33 @@ * @author Simone Giannecchini, GeoSolutions SAS * @version $Id$ */ -public class TimeKvpParser extends KvpParser { - /** - * All patterns that are correct regarding the ISO-8601 norm. - */ - private static final String[] PATTERNS = { - "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", - "yyyy-MM-dd'T'HH:mm:sss'Z'", - "yyyy-MM-dd'T'HH:mm:ss'Z'", - "yyyy-MM-dd'T'HH:mm'Z'", - "yyyy-MM-dd'T'HH'Z'", - "yyyy-MM-dd", - "yyyy-MM", - "yyyy" - }; +public class TimeKvpParser extends KvpParser { + private static enum FormatAndPrecision { + MILLISECOND("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Calendar.MILLISECOND), + SECOND("yyyy-MM-dd'T'HH:mm:ss'Z'", Calendar.SECOND), + MINUTE("yyyy-MM-dd'T'HH:mm'Z'", Calendar.MINUTE), + HOUR("yyyy-MM-dd'T'HH'Z'", Calendar.HOUR_OF_DAY), + DAY("yyyy-MM-dd", Calendar.DAY_OF_MONTH), + MONTH("yyyy-MM", Calendar.MONTH), + YEAR("yyyy", Calendar.YEAR); + + public final SimpleDateFormat format; + public final int precision; + + FormatAndPrecision(String format, int precision) { + this.format = new SimpleDateFormat(format); + this.format.setTimeZone(UTC_TZ); + this.precision = precision; + } + + public DateRange expand(Date d) { + Calendar c = new GregorianCalendar(UTC_TZ); + c.setTime(d); + c.add(this.precision, 1); + c.add(Calendar.MILLISECOND, -1); + return new DateRange(d, c.getTime()); + } + } /** * UTC timezone to serve as reference @@ -149,7 +165,7 @@ public int compare(Object o1, Object o2) { for(String date: listDates){ // is it a date or a period? if(date.indexOf("/")<=0){ - addDate(result,getDate(date)); + addPeriod(result,getFuzzyDate(date)); } else { // period String[] period = date.split("/"); @@ -157,17 +173,17 @@ public int compare(Object o1, Object o2) { // Period like : yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ/P1D // if (period.length == 3) { - final Date begin = getDate(period[0]); - final Date end = getDate(period[1]); + final Date begin = getFuzzyDate(period[0]).getMinValue(); + final Date end = getFuzzyDate(period[1]).getMaxValue(); final long millisIncrement = parsePeriod(period[2]); final long startTime = begin.getTime(); final long endTime = end.getTime(); long time; int j = 0; while ((time = j * millisIncrement + startTime) <= endTime) { - final Calendar calendar = Calendar.getInstance(UTC_TZ); + final Calendar calendar = new GregorianCalendar(UTC_TZ); calendar.setTimeInMillis(time); - addDate(result,calendar.getTime()); + addPeriod(result, new DateRange(calendar.getTime(), calendar.getTime())); j++; // limiting number of elements we can create @@ -180,9 +196,9 @@ public int compare(Object o1, Object o2) { } else if (period.length == 2) { // Period like : yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ, it is an extension // of WMS that works with continuos period [Tb, Te]. - final Date begin = getDate(period[0]); - final Date end = getDate(period[1]); - addPeriod(result,new DateRange(begin, end)); + final Date begin = getFuzzyDate(period[0]).getMinValue(); + final Date end = getFuzzyDate(period[1]).getMaxValue(); + addPeriod(result, new DateRange(begin, end)); } else { throw new ParseException("Invalid time period: " + Arrays.toString(period), 0); } @@ -193,30 +209,6 @@ public int compare(Object o1, Object o2) { } - /** - * Tries to avoid insertion of multiple time values. - * - * @param result - * @param time - */ - private static void addDate(Collection result, Date time) { - for(Iterator it=result.iterator();it.hasNext();){ - final Object element=it.next(); - if(element instanceof Date){ - // convert - final Date local= (Date) element; - if(local.equals(time)) - return; - } else { - // convert - final DateRange local= (DateRange) element; - if(local.contains(time)) - return; - } - } - result.add(time); - } - /** * Tries to avoid insertion of multiple time values. * @@ -253,27 +245,21 @@ private static void addPeriod(Collection result, DateRange newRange) { * @return A date found in the request. * @throws ParseException if the string can not be parsed. */ - private static Date getDate(final String value) throws ParseException { + static DateRange getFuzzyDate(final String value) throws ParseException { // special handling for current keyword (we accept both wms and wcs ways) if(value.equalsIgnoreCase("current") || value.equalsIgnoreCase("now")) { return null; } - for (int i=0; i times = request.getTime(); assertEquals(1, request.getTime().size()); - assertEquals(cal.getTime().toString(), times.get(0).toString()); + assertEquals(cal.getTime(), ((DateRange)times.get(0)).getMinValue()); } public void testDefaultStyle() throws Exception { From 638f1238b3ed60993dd5e17f68afef6a0dcf6216 Mon Sep 17 00:00:00 2001 From: David Winslow Date: Thu, 23 Aug 2012 21:38:59 -0400 Subject: [PATCH 43/72] Revert "Support reduced accuracy in WMS TIME parameter" This reverts commit 9a5e438e3214af41813fc785a5ab79ceedf8ed66. --- doc/en/user/source/services/wms/index.rst | 1 - doc/en/user/source/services/wms/reference.rst | 7 - doc/en/user/source/services/wms/time.rst | 188 ---------------- .../org/geoserver/ows/kvp/TimeKvpParser.java | 112 +++++----- .../geoserver/ows/kvp/TimeKvpParserTest.java | 208 +++--------------- .../wms/WMSDimensionsTestSupport.java | 2 +- .../wms/map/GetMapKvpRequestReaderTest.java | 8 +- 7 files changed, 103 insertions(+), 423 deletions(-) delete mode 100644 doc/en/user/source/services/wms/time.rst diff --git a/doc/en/user/source/services/wms/index.rst b/doc/en/user/source/services/wms/index.rst index 50923b392d6..81158874d09 100644 --- a/doc/en/user/source/services/wms/index.rst +++ b/doc/en/user/source/services/wms/index.rst @@ -8,7 +8,6 @@ Web Map Service basics reference - time outputformats vendor configuration diff --git a/doc/en/user/source/services/wms/reference.rst b/doc/en/user/source/services/wms/reference.rst index 0a9ba171771..b407cec649d 100644 --- a/doc/en/user/source/services/wms/reference.rst +++ b/doc/en/user/source/services/wms/reference.rst @@ -199,13 +199,6 @@ An example request for a PNG map image using default styling is: &height=330 &format=image%2Fpng -Time -.... - -As of GeoServer 2.2.0, GeoServer supports a TIME attribute for WMS GetMap requests as described in version 1.3 of the WMS specification. -This parameter allows filtering a dataset by temporal slices as well as spatial tiles for rendering. -See :doc:`/services/wms/time` for information on its use. - .. _wms_getfeatureinfo: diff --git a/doc/en/user/source/services/wms/time.rst b/doc/en/user/source/services/wms/time.rst deleted file mode 100644 index c59a41f7dea..00000000000 --- a/doc/en/user/source/services/wms/time.rst +++ /dev/null @@ -1,188 +0,0 @@ -Time Support in Geoserver WMS -============================= - -For layers that are properly configured with a TIME dimension, GeoServer supports a TIME attribute in GetMap requests to specify a temporal subset for rendering. -For example, you might have a single dataset with weather observations collected over time and choose to plot a single day's worth of observations. - -Specifying a Time ------------------ - -The format used for specifying a time in the WMS TIME parameter is based on `ISO-8601 `_. -Times may be specified to a precision of 1 millisecond; GeoServer does not represent time queries with more precision than this. -Times follow the general format:: - - yyyy-MM-ddThh:mm:ss.SSSZ - -That is, a day specified by a 4-digit year, 2-digit month, and 2-digit day-of-month field, and an instant on that day specified by 2-digit hour, minute, and second fields, with an arbitrary number of decimal digits after the seconds field. -The day and instant seconds are separated with a capital 'T', and the entire thing is suffixed with a 'Z' (indicating 'Zulu' or `UTC ` for the time zone. The WMS specification does not provide for other time zones.) - -GeoServer will apply the TIME value to all temporally enabled layers in the LAYERS parameter of the GetMap request. -Layers without a temporal component will be served normally - allowing clients to include reference information like political boundaries along with temporal data. - -Examples -........ - - * December 12, 2001 at 6:00 PM would be represented as:: - - TIME=2001-12-12T18:00:00.0Z - - * May 5, 1993 at 11:34 PM would be represented as:: - - TIME=1993-05-05T11:34:00.0Z - -Specifying a Periodicity ------------------------- - -The periodicity is also specified in ISO-8601 format: a capital P followed by one or more interval lengths, each consisting of a number and a letter identifying a time unit: - -.. list-table:: - - * - **Unit** - - **Abbreviation** - * - Years - - ``Y`` - * - Months - - ``M`` - * - Days - - ``D`` - * - Hours - - ``H`` - * - Minutes - - ``M`` - * - Seconds - - ``S`` - -The Year/Month/Day group of values must be separated from the Hours/Minutes/Seconds group by a ``T`` character. -Additionally, fields which contain a 0 may be omitted entirely, and the T may be omitted if hours, minutes, and seconds are all omitted. -Fractional values are permitted, but only for the most specific value that is included. - -The period must divide evenly into the interval defined by the start/end times. - -Examples of Periods -................... - - * One hour:: - - P0Y0M0DT1H0M0S - - PT1H0M0S - - PT1H - - * 90 minutes (equivalent to 1 hour, 30 minutes):: - - P0Y0M0DT1H30M0S - - PT1H30M - - P90M - - * 18 months:: - - P1Y6M0DT0H0M0S - - P1Y6M0D - - P0Y18M0DT0H0M0S - - P18M - - *But not* `` P1.25Y3M - -Specifying an Interval ----------------------- - -A client may request information over a continuous interval instead of a single instant by specifying a start and end time, separated by a ``/`` character. -In this scenario the start and end are both inclusive; that is, samples from exactly the endpoints of the specified range will be included in the rendered tile. - -Examples -........ - - -.. list-table:: - - * - Description - - Time specification - * - The month of September 2002 - - ``2002-09-01T00:00:00.0Z/2002-10-01T23:59:59.999Z`` - * - The entire day of December 25, 2010 - - ``2010-12-25T00:00:00.0Z/2010-12-25T23:59:59.999Z`` - -.. note:: - - Because the time interval is inclusive, we cannot precisely specify a concept such as "all times within day x". - We must choose between incorrectly accepting observations that occur at the end point, and incorrectly excluding some fraction of the final second of the interval. - In practice, GeoServer and many data storage engines have limited resolution in their representations, so approximating a range to the nearest millisecond is 'as good as we can do.' - It is possible that this technical constraint may be lifted at some point in the future. - -Reduced Accuracy Times ----------------------- - -The WMS specification also allows time specifications to be truncated, by omitting some suffix of the time string. -In this case, GeoServer treats the time as a range whose length is equal to one of the most precise unit specified in the time string. -If time specification omits all fields except year, it identifies a range one year long starting at the beginning of that year, etc. - -GeoServer implements this by adding the appropriate unit, then subtracting 1 millisecond. -This avoids surprising results when using an interval that aligns with the actual sampling frequency of the data - for example, if yearly data is natively stored with dates like 2001-01-01T00:00:00.0Z, 2002-01-01T00:00:00Z, etc. then a request for 2001 would include the samples for both 2001 and 2002 otherwise. - -Examples -........ - -.. list-table:: - - * - Description - - Reduced Accuracy Time - - Equivalent Range - * - The month of September 2002 - - ``2002-09`` - - ``2002-09-01T00:00:00.0Z/2002-10-01T23:59:59.999Z`` - * - The day of December 25, 2010:: - - ``2010-12-25`` - - ``2010-12-25T00:00:00.0Z/2010-12-25T23:59:59.999Z`` - -Ranges with Reduced Accuracy Times ----------------------------------- - -Reduced accuracy times are also allowed when specifying ranges. -In this case, GeoServer effectively expands the start and end times as described above, and then includes any samples from after the beginning of the start interval and before the end of the end interval. - -.. list-table:: - - * - Description - - Reduced Accuracy Time - - Equivalent Range - * - The months of September through December 2002 - - ``2002-09/2002-12`` - - ``2002-09-01T00:00:00.0Z/2002-12-31T23:59:59.999Z`` - * - 12pm through 6pm, December 25, 2010:: - - ``2010-12-25T12/2010-12-25T18`` - - ``2010-12-25T12:00:00.0Z/2010-12-25T18:59:59.999Z`` - -Specifying a List of Times --------------------------- - -For some formats, GeoServer can generate an animation. -In this case, the client must specify multiple times, one for each frame. -When multiple times are needed, the client should simply format each time as described above, and separate them with commas. - -If the list is evenly spaced (for example, daily or hourly samples) then the list may be specified as a range, using a start time, end time, and period separated by slashes. - -Examples -........ - -.. list-table:: - - * - Description - - List notation - - Range notation - * - Noon every day for the week of August 12-18, 2012:: - - ``TIME=2012-08-12T12:00:00.0Z,2012-08-13T12:00:00.0Z,2012-08-14T12:00:00.0Z,2012-08-15T12:00:00.0Z,2012-08-16T12:00:00.0Z,2012-08-17T12:00:00.0Z,2012-08-18T12:00:00.0Z`` - - ``TIME=2012-08-12T12:00:00.0Z/2012-08-18:T12:00:00.0Z/P1D`` - * - Midnight on the first of September, October, and November 1999:: - - ``TIME=1999-09-01T00:00:00.0Z,1999-10-01T00:00:00.0Z,1999-11-01T00:00:00.0Z - - ``TIME=1999-09-01T00:00:00.0Z/1999-11-01T00:00:00.0Z/P1M`` - -.. note:: - - GeoServer currently does not support lists of ranges, so all list queries effectively have a resolution of 1 millisecond. - If you use reduced accuracy notation when specifying a range, each range will be automatically converted to the instant at the beginning of the range. diff --git a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java index 8020fb1df73..f1e70200f2f 100644 --- a/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java +++ b/src/ows/src/main/java/org/geoserver/ows/kvp/TimeKvpParser.java @@ -14,7 +14,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; -import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -22,8 +21,6 @@ import java.util.TimeZone; import java.util.TreeSet; import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.geoserver.ows.KvpParser; import org.geoserver.platform.GeoServerExtensions; @@ -39,33 +36,20 @@ * @author Simone Giannecchini, GeoSolutions SAS * @version $Id$ */ -public class TimeKvpParser extends KvpParser { - private static enum FormatAndPrecision { - MILLISECOND("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Calendar.MILLISECOND), - SECOND("yyyy-MM-dd'T'HH:mm:ss'Z'", Calendar.SECOND), - MINUTE("yyyy-MM-dd'T'HH:mm'Z'", Calendar.MINUTE), - HOUR("yyyy-MM-dd'T'HH'Z'", Calendar.HOUR_OF_DAY), - DAY("yyyy-MM-dd", Calendar.DAY_OF_MONTH), - MONTH("yyyy-MM", Calendar.MONTH), - YEAR("yyyy", Calendar.YEAR); - - public final SimpleDateFormat format; - public final int precision; - - FormatAndPrecision(String format, int precision) { - this.format = new SimpleDateFormat(format); - this.format.setTimeZone(UTC_TZ); - this.precision = precision; - } - - public DateRange expand(Date d) { - Calendar c = new GregorianCalendar(UTC_TZ); - c.setTime(d); - c.add(this.precision, 1); - c.add(Calendar.MILLISECOND, -1); - return new DateRange(d, c.getTime()); - } - } +public class TimeKvpParser extends KvpParser { + /** + * All patterns that are correct regarding the ISO-8601 norm. + */ + private static final String[] PATTERNS = { + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "yyyy-MM-dd'T'HH:mm:sss'Z'", + "yyyy-MM-dd'T'HH:mm:ss'Z'", + "yyyy-MM-dd'T'HH:mm'Z'", + "yyyy-MM-dd'T'HH'Z'", + "yyyy-MM-dd", + "yyyy-MM", + "yyyy" + }; /** * UTC timezone to serve as reference @@ -165,7 +149,7 @@ public int compare(Object o1, Object o2) { for(String date: listDates){ // is it a date or a period? if(date.indexOf("/")<=0){ - addPeriod(result,getFuzzyDate(date)); + addDate(result,getDate(date)); } else { // period String[] period = date.split("/"); @@ -173,17 +157,17 @@ public int compare(Object o1, Object o2) { // Period like : yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ/P1D // if (period.length == 3) { - final Date begin = getFuzzyDate(period[0]).getMinValue(); - final Date end = getFuzzyDate(period[1]).getMaxValue(); + final Date begin = getDate(period[0]); + final Date end = getDate(period[1]); final long millisIncrement = parsePeriod(period[2]); final long startTime = begin.getTime(); final long endTime = end.getTime(); long time; int j = 0; while ((time = j * millisIncrement + startTime) <= endTime) { - final Calendar calendar = new GregorianCalendar(UTC_TZ); + final Calendar calendar = Calendar.getInstance(UTC_TZ); calendar.setTimeInMillis(time); - addPeriod(result, new DateRange(calendar.getTime(), calendar.getTime())); + addDate(result,calendar.getTime()); j++; // limiting number of elements we can create @@ -196,9 +180,9 @@ public int compare(Object o1, Object o2) { } else if (period.length == 2) { // Period like : yyyy-MM-ddTHH:mm:ssZ/yyyy-MM-ddTHH:mm:ssZ, it is an extension // of WMS that works with continuos period [Tb, Te]. - final Date begin = getFuzzyDate(period[0]).getMinValue(); - final Date end = getFuzzyDate(period[1]).getMaxValue(); - addPeriod(result, new DateRange(begin, end)); + final Date begin = getDate(period[0]); + final Date end = getDate(period[1]); + addPeriod(result,new DateRange(begin, end)); } else { throw new ParseException("Invalid time period: " + Arrays.toString(period), 0); } @@ -209,6 +193,30 @@ public int compare(Object o1, Object o2) { } + /** + * Tries to avoid insertion of multiple time values. + * + * @param result + * @param time + */ + private static void addDate(Collection result, Date time) { + for(Iterator it=result.iterator();it.hasNext();){ + final Object element=it.next(); + if(element instanceof Date){ + // convert + final Date local= (Date) element; + if(local.equals(time)) + return; + } else { + // convert + final DateRange local= (DateRange) element; + if(local.contains(time)) + return; + } + } + result.add(time); + } + /** * Tries to avoid insertion of multiple time values. * @@ -245,21 +253,27 @@ private static void addPeriod(Collection result, DateRange newRange) { * @return A date found in the request. * @throws ParseException if the string can not be parsed. */ - static DateRange getFuzzyDate(final String value) throws ParseException { + private static Date getDate(final String value) throws ParseException { // special handling for current keyword (we accept both wms and wcs ways) if(value.equalsIgnoreCase("current") || value.equalsIgnoreCase("now")) { return null; } - - for (FormatAndPrecision f : FormatAndPrecision.values()) { - ParsePosition pos = new ParsePosition(0); - Date time = f.format.parse(value, pos); - if (pos.getIndex() == value.length()) { - return f.expand(time); - } - } - + for (int i=0; i times = request.getTime(); assertEquals(1, request.getTime().size()); - assertEquals(cal.getTime(), ((DateRange)times.get(0)).getMinValue()); + assertEquals(cal.getTime().toString(), times.get(0).toString()); } public void testDefaultStyle() throws Exception { From 04d20e3f6efd0625e74d5988d20746a1ce90a68e Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Fri, 24 Aug 2012 14:33:43 +0200 Subject: [PATCH 44/72] Adding security information on the welcome page of an admin --- ...ntProvider$PasswordChangeWarningPanel.html | 10 -- ...ContentProvider$SecurityWarningsPanel.html | 26 ++++ .../web/SecurityHomePageContentProvider.java | 145 ++++++++++++++---- .../resources/GeoServerApplication.properties | 13 +- 4 files changed, 149 insertions(+), 45 deletions(-) delete mode 100644 src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$PasswordChangeWarningPanel.html create mode 100644 src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$SecurityWarningsPanel.html diff --git a/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$PasswordChangeWarningPanel.html b/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$PasswordChangeWarningPanel.html deleted file mode 100644 index 4a0f3044e74..00000000000 --- a/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$PasswordChangeWarningPanel.html +++ /dev/null @@ -1,10 +0,0 @@ - - - -
          - - -
          -
          - - \ No newline at end of file diff --git a/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$SecurityWarningsPanel.html b/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$SecurityWarningsPanel.html new file mode 100644 index 00000000000..4b90a9acb08 --- /dev/null +++ b/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider$SecurityWarningsPanel.html @@ -0,0 +1,26 @@ + + + +

          + +

          +

          + +

          +

          + +

          +

          + + +

          +

          + + +

          +

          + +

          +
          + + \ No newline at end of file diff --git a/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider.java b/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider.java index a025defa578..928052accc2 100644 --- a/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider.java +++ b/src/web/security/src/main/java/org/geoserver/security/web/SecurityHomePageContentProvider.java @@ -8,21 +8,27 @@ import static org.geoserver.security.impl.GeoServerUser.DEFAULT_ADMIN_PASSWD; import static org.geoserver.security.impl.GeoServerUser.ROOT_USERNAME; +import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.wicket.Component; import org.apache.wicket.Page; +import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; import org.geoserver.security.GeoServerSecurityManager; import org.geoserver.security.GeoServerUserGroupService; +import org.geoserver.security.config.SecurityUserGroupServiceConfig; import org.geoserver.security.impl.GeoServerUser; +import org.geoserver.security.password.GeoServerPasswordEncoder; import org.geoserver.security.web.passwd.MasterPasswordChangePage; import org.geoserver.security.web.user.EditUserPage; +import org.geoserver.security.xml.XMLUserGroupService; import org.geoserver.web.GeoServerApplication; import org.geoserver.web.GeoServerHomePageContentProvider; import org.geotools.util.logging.Logging; @@ -39,34 +45,12 @@ public Component getPageBodyComponent(String id) { //do a check that the root password is not set GeoServerSecurityManager secMgr = GeoServerApplication.get().getSecurityManager(); if (secMgr.checkAuthenticationForAdminRole()) { - if (testAuthentication(ROOT_USERNAME, DEFAULT_ADMIN_PASSWD, secMgr)) { - return new PasswordChangeWarningPanel(id, "changeMasterPassword", - new MasterPasswordChangePage()); - } - else if (testAuthentication(ADMIN_USERNAME, "geoserver", secMgr)) { - Page changeItPage = null; - try { - GeoServerUserGroupService ugService = secMgr.loadUserGroupService("default"); - if (ugService != null) { - GeoServerUser user = ugService.getUserByUsername(ADMIN_USERNAME); - if (user != null) { - changeItPage = new EditUserPage(ugService.getName(), user); - } - } - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Error looking up admin user", e); - } - if (changeItPage == null) { - changeItPage = new UserGroupRoleServicesPage(); - } - return new PasswordChangeWarningPanel(id, "changeAdminPassword", changeItPage); - } + return new SecurityWarningsPanel(id); } - return null; } - boolean testAuthentication(String user, String passwd, GeoServerSecurityManager secMgr) { + static boolean testAuthentication(String user, String passwd, GeoServerSecurityManager secMgr) { Authentication token = new UsernamePasswordAuthenticationToken(user, passwd); try { token = secMgr.authenticate(token); @@ -76,20 +60,119 @@ boolean testAuthentication(String user, String passwd, GeoServerSecurityManager } return token.isAuthenticated(); } + // PasswordChangeWarningPanel + static class SecurityWarningsPanel extends Panel { - static class PasswordChangeWarningPanel extends Panel { - - public PasswordChangeWarningPanel(String id, String messageKey, final Page changeItPage) { + public SecurityWarningsPanel(String id) { super(id); - add(new Label("message", new StringResourceModel(messageKey, (Component)this, null)) - .setEscapeModelStrings(false)); - add(new Link("link") { + GeoServerSecurityManager manager = GeoServerApplication.get().getSecurityManager(); + + // warn in case of an existing masterpw.info + File mpInfo = null; + Label mpInfoLabel=null; + try { + mpInfo = new File (manager.getSecurityRoot(), + GeoServerSecurityManager.MASTER_PASSWD_INFO_FILENAME); + mpInfoLabel=new Label("mpfile", new StringResourceModel("masterPasswordFile", (Component)this, null, + new Object[] {mpInfo.getCanonicalFile()})); + mpInfoLabel.setEscapeModelStrings(false); + add(mpInfoLabel); + mpInfoLabel.setVisible(mpInfo.exists()); + } catch (Exception ex) { + throw new RuntimeException (ex); + } + + // warn in case of an existing user.properties.old + File userprops = null; + Label userpropsLabel=null; + try { + userprops = new File (manager.getSecurityRoot(), + "users.properties.old"); + userpropsLabel=new Label("userpropsold", new StringResourceModel("userPropertiesOldFile", (Component)this, null, + new Object[] {userprops.getCanonicalFile()})); + userpropsLabel.setEscapeModelStrings(false); + add(userpropsLabel); + userpropsLabel.setVisible(userprops.exists()); + } catch (Exception ex) { + throw new RuntimeException (ex); + } + + // check for default master password + boolean visibility = testAuthentication(ROOT_USERNAME, DEFAULT_ADMIN_PASSWD, manager); + + Label label=new Label("mpmessage", new StringResourceModel("changeMasterPassword", (Component)this, null)); + label.setEscapeModelStrings(false); + add(label); + Link link=null;; + add(link=new Link("mplink") { + @Override + public void onClick() { + setResponsePage(new MasterPasswordChangePage()); + } + }); + label.setVisible(visibility); + link.setVisible(visibility); + + + + // check for default admin password + visibility= testAuthentication(ADMIN_USERNAME, DEFAULT_ADMIN_PASSWD, manager); + Page changeItPage = null; + String passwordEncoderName=null; + try { + GeoServerUserGroupService ugService = manager.loadUserGroupService(XMLUserGroupService.DEFAULT_NAME); + if (ugService != null) { + passwordEncoderName = ugService.getPasswordEncoderName(); + GeoServerUser user = ugService.getUserByUsername(ADMIN_USERNAME); + if (user != null) { + changeItPage = new EditUserPage(ugService.getName(), user); + } + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Error looking up admin user", e); + } + if (changeItPage == null) { + changeItPage = new UserGroupRoleServicesPage(); + } + + + final Page linkPage = changeItPage; + label=new Label("adminmessage", new StringResourceModel("changeAdminPassword", (Component)this, null)); + label.setEscapeModelStrings(false); + add(label); + add(link=new Link("adminlink") { @Override public void onClick() { - setResponsePage(changeItPage); + setResponsePage(linkPage); } }); + label.setVisible(visibility); + link.setVisible(visibility); + + // inform about strong encryption + if (manager.isStrongEncryptionAvailable()) { + add(new Label("strongEncryptionMsg", new StringResourceModel("strongEncryption", new SecuritySettingsPage(), null)) + .add(new AttributeAppender("class", new Model("info-link"), " "))); + } + else { + add(new Label("strongEncryptionMsg", new StringResourceModel("noStrongEncryption", new SecuritySettingsPage(), null)) + .add(new AttributeAppender("class", new Model("warning-link"), " "))); + } + + // check for password encoding in the default user group service + visibility=false; + if (passwordEncoderName!=null) { + GeoServerPasswordEncoder encoder = manager.loadPasswordEncoder(passwordEncoderName); + if (encoder!=null) { + visibility = encoder.isReversible(); + } + } + + label=new Label("digestEncoding", new StringResourceModel("digestEncoding", (Component)this, null)); + add(label); + label.setVisible(visibility); + } } } diff --git a/src/web/security/src/main/resources/GeoServerApplication.properties b/src/web/security/src/main/resources/GeoServerApplication.properties index f42d5f52f99..692118b97e1 100644 --- a/src/web/security/src/main/resources/GeoServerApplication.properties +++ b/src/web/security/src/main/resources/GeoServerApplication.properties @@ -578,9 +578,14 @@ URLMasterPasswordProviderPanel.urlHelp.title=URL Master Password Provider URLMasterPasswordProviderPanel.urlHelp=

          The URL Master Password Provider obtains the master password \ from a URL. The URL may point to a local file, or an external resource or service. Non read-only \ urls offer the option to encrypt the password to/from the URL source.

          - -PasswordChangeWarningPanel.changeMasterPassword=The master password for this server has not been changed from \ + +SecurityWarningsPanel.userPropertiesOldFile=Please remove the file {0} because it contains user passwords in plain text. This file \ +is a security risk. +SecurityWarningsPanel.masterPasswordFile=Please read the file {0} and remove it afterwards. This file \ +is a security risk. +SecurityWarningsPanel.digestEncoding=The default user/group service should use digest password encoding. +SecurityWarningsPanel.changeMasterPassword=The master password for this server has not been changed from \ the default. It is highly recommended that you change it now. -PasswordChangeWarningPanel.changeAdminPassword=The administrator password for this server has not been changed from \ +SecurityWarningsPanel.changeAdminPassword=The administrator password for this server has not been changed from \ the default. It is highly recommended that you change it now. -PasswordChangeWarningPanel.changeIt=Change it \ No newline at end of file +SecurityWarningsPanel.changeIt=Change it \ No newline at end of file From 9e9865d09ba8fb5eb19bdf27e03c1a247e6e93ff Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Fri, 24 Aug 2012 15:05:42 +0200 Subject: [PATCH 45/72] fixing GEOS-5281 --- doc/en/user/source/security/usergrouprole/roleservices.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/user/source/security/usergrouprole/roleservices.rst b/doc/en/user/source/security/usergrouprole/roleservices.rst index eff6a7fc5b9..fcef113de4e 100644 --- a/doc/en/user/source/security/usergrouprole/roleservices.rst +++ b/doc/en/user/source/security/usergrouprole/roleservices.rst @@ -56,7 +56,7 @@ JDBC role service The JDBC role service persists the role database via JDBC. It represents the role database with multiple tables. The following shows the database schema: -.. list-table:: Table: users +.. list-table:: Table: roles :widths: 15 15 15 15 :header-rows: 1 From 6a971832224180f015a9d0a0d92f2629bcb4de47 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Fri, 24 Aug 2012 15:28:04 +0200 Subject: [PATCH 46/72] Fixing build --- .../org/geoserver/data/test/MockData.java | 41 ++++++++++++++++++ .../java/org/geoserver/data/test/rain.zip | Bin 5447 -> 5553 bytes .../wcs/DefaultWebCoverageService111.java | 14 +++--- .../org/geoserver/wcs/GetCoverageTest.java | 4 +- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/main/src/test/java/org/geoserver/data/test/MockData.java b/src/main/src/test/java/org/geoserver/data/test/MockData.java index 7907fd0a07d..d1fdf1605c8 100644 --- a/src/main/src/test/java/org/geoserver/data/test/MockData.java +++ b/src/main/src/test/java/org/geoserver/data/test/MockData.java @@ -628,6 +628,47 @@ public void addCoverage(QName name, URL coverage, String extension, String style coverageStores.put(name.getLocalPart(), params); } + public void addCoverageFromZip(QName name, URL coverage, String extension, String styleName) throws Exception { + File directory = new File(data, name.getPrefix()); + if (!directory.exists()) { + directory.mkdir(); + } + + File f = new File(directory, name.getLocalPart()); + f.mkdir(); + + File compressedFile = new File(f, name.getLocalPart() + ".zip"); + IOUtils.copy(coverage.openStream(), compressedFile); + IOUtils.decompress(compressedFile, f); + final File srcDir = new File(f, name.getLocalPart()); + srcDir.mkdir(); + FileUtils.copyDirectory(srcDir, f, true); + + if (extension != null) { + File coverageFile = new File(srcDir, name.getLocalPart() + "." + extension); + addCoverageFromPath(name, coverageFile, + "file:" + name.getPrefix() + "/" + name.getLocalPart() + "/" + name.getLocalPart() + "." + extension, + styleName); + } else { + addCoverageFromPath(name, f, + "file:" + name.getPrefix() + "/" + name.getLocalPart(), + styleName); + } + } + + private void addCoverageFromPath(QName name, File coverage, String relpath, String styleName) throws Exception { + coverageInfo(name, coverage, styleName); + + // setup the meta information to be written in the catalog + AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat(coverage); + namespaces.put(name.getPrefix(), name.getNamespaceURI()); + coverageStoresNamespaces.put(name.getLocalPart(), name.getPrefix()); + Map params = new HashMap(); + params.put(CatalogWriter.COVERAGE_TYPE_KEY, format.getName()); + params.put(CatalogWriter.COVERAGE_URL_KEY, relpath); + coverageStores.put(name.getLocalPart(), params); + } + /** * Disables the specificed datastore (it's still configured, but with enabled=false) * @param datastoreId diff --git a/src/main/src/test/java/org/geoserver/data/test/rain.zip b/src/main/src/test/java/org/geoserver/data/test/rain.zip index adf566632b89fe502ba95d6c6d7c9ae8a7d5423f..b7c9ba84448b731db22a73e18956bb524cf4ac0f 100644 GIT binary patch delta 225 zcmX@EwNYC+z?+$ciD9Q@(W@Q6uWd_2PK>Cvihz9_2Vk+VQ delta 142 zcmdm}eOyZ}z?+#xgqeYXgW-3ynB&g(Jg4ReGcZij0f{g$Y;@Zv%mQLe<`sFZfm_}XlPMlW}aR_QC5IAD;r3L2?%q5^idHI4*=Qa9CZKy diff --git a/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java b/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java index 6a656e0d2b8..2113fe7ff12 100644 --- a/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java +++ b/src/wcs1_1/src/main/java/org/geoserver/wcs/DefaultWebCoverageService111.java @@ -209,9 +209,9 @@ public GridCoverage[] getCoverage(GetCoverageType request) { double[] elevations = null; // grab the grid to world transformation - MathTransform gridToCRS = reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER); - double pixelSizeX = 1; - double pixelSizeY = 1; + MathTransform gridToCRS = reader.getOriginalGridToWorld(PixelInCell.CELL_CENTER); + double pixelSizeX; + double pixelSizeY; if (gridCRS != null) { Double[] origin = (Double[]) gridCRS.getGridOrigin(); Double[] offsets = (Double[]) gridCRS.getGridOffsets(); @@ -293,6 +293,10 @@ public GridCoverage[] getCoverage(GetCoverageType request) { pixelSizeX = Math.abs(tx.getScaleX()); pixelSizeY = Math.abs(tx.getScaleY()); gridToCRS = new AffineTransform2D(tx); + } else { + AffineTransform2D at = (AffineTransform2D) gridToCRS; + pixelSizeX = Math.abs(at.getScaleX()); + pixelSizeY = Math.abs(at.getScaleY()); } // @@ -326,7 +330,7 @@ public GridCoverage[] getCoverage(GetCoverageType request) { intersectionEnvelopeInSourceCRS.intersect(originalEnvelope); - final GridGeometry2D requestedGridGeometry = new GridGeometry2D(PixelInCell.CELL_CORNER, gridToCRS, intersectionEnvelopeInSourceCRS, null); + final GridGeometry2D requestedGridGeometry = new GridGeometry2D(PixelInCell.CELL_CENTER, gridToCRS, intersectionEnvelopeInSourceCRS, null); final ParameterValueGroup readParametersDescriptor = reader.getFormat().getReadParameters(); GeneralParameterValue[] readParameters = CoverageUtils.getParameters(readParametersDescriptor, meta.getParameters()); @@ -521,7 +525,7 @@ public GridCoverage[] getCoverage(GetCoverageType request) { final GridCoverage2D reprojectedCoverage = WCSUtils.resample( bandSelectedCoverage, nativeCRS, targetCRS, destinationGridGeometry,interpolation); - + return new GridCoverage[] { reprojectedCoverage }; } else { return new GridCoverage[] { bandSelectedCoverage }; diff --git a/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java b/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java index 1eee5c4c825..08f77b5c206 100644 --- a/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java +++ b/src/wcs1_1/src/test/java/org/geoserver/wcs/GetCoverageTest.java @@ -65,8 +65,8 @@ protected void populateDataDirectory(MockData dataDirectory) throws Exception { MockData.class.getResource("raster-filter-test.zip"), null, "raster"); // add a data source that cannot restrict spatial data on its own - dataDirectory.addCoverage(RAIN, - MockData.class.getResource("rain.zip"), null, "raster"); + dataDirectory.addCoverageFromZip(RAIN, + MockData.class.getResource("rain.zip"), "asc", "raster"); } public void testKvpBasic() throws Exception { From 1179e222da76f8dafba8f885fbed1807184ea050 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sat, 25 Aug 2012 10:21:40 +0200 Subject: [PATCH 47/72] GEOS-5278 --- doc/en/user/source/production/linuxscript.rst | 5 + .../production/scripts/geoserver_tomcat | 139 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 doc/en/user/source/production/scripts/geoserver_tomcat diff --git a/doc/en/user/source/production/linuxscript.rst b/doc/en/user/source/production/linuxscript.rst index 465eaf6f80e..ced8885a707 100644 --- a/doc/en/user/source/production/linuxscript.rst +++ b/doc/en/user/source/production/linuxscript.rst @@ -30,3 +30,8 @@ Suse :download:`Download the init script ` +Starting GeoServer in Tomcat +---------------------------- + +:download:`Download the init script ` + diff --git a/doc/en/user/source/production/scripts/geoserver_tomcat b/doc/en/user/source/production/scripts/geoserver_tomcat new file mode 100644 index 00000000000..1a8cff290a7 --- /dev/null +++ b/doc/en/user/source/production/scripts/geoserver_tomcat @@ -0,0 +1,139 @@ +#!/bin/bash +# +# This shell script takes care of starting and stopping Geoserver deployed in Apache Tomcat +# It also handles killing Geoserver in case it doesn’t stop gracefully +# It uses a PID file to determine the process ID so it should work with multiple Tomcat instances on one server +# Just copy the script and change $INSTANCE variable to run multiple Geoserver instances +# Tested on Ubuntu 12.04 +# +# chkconfig: - 80 20 +# +### BEGIN INIT INFO +# Provides: Geoserver +# Required-Start: $network $syslog +# Required-Stop: $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Description: Geoserver service +# Short-Description: start and stop tomcat +### END INIT INFO +# +# Author dragan.podvezanec@gmail.com +# +# Based on work by Yoryos Valotasios + + +# Geoserver configuration section +INSTANCE=1 +USER=geoserver +GEOSERVER_DATA_DIR=/home/geoserver/data_dir +GEOSERVER_HOME=/home/geoserver/geoserver$INSTANCE + +export GEOSERVER_DATA_DIR=$GEOSERVER_DATA_DIR +export GEOSERVER_HOME=$GEOSERVER_HOME + +# Uncomment this if you need custom logging location +#export GEOSERVER_LOG_LOCATION=$GEOSERVER_HOME/logs/geoserver.log + +# Java configuration section +JAVA_HOME=/home/geoserver/jre +JAVA_OPTS="-Xms256m -Xmx2048m -XX:MaxPermSize=512m -XX:+UseParallelGC -XX:+UseParallelOldGC" +CATALINA_PID=/var/run/geoserver$INSTANCE.pid + +export JAVA_HOME=$JAVA_HOME +export JAVA_OPTS=$JAVA_OPTS +export CATALINA_PID=$CATALINA_PID +export PATH=$JAVA_HOME/bin:$PATH +# End configuration section + +# Number of seconds to wait after nicely requesting stop +SHUTDOWN_WAIT=10 + +geoserver_pid(){ + echo `ps aux | grep org.apache.catalina.startup.Bootstrap | grep $GEOSERVER_HOME | grep -v grep | awk '{ print $2 }'` +} + +start() { +pid=$(geoserver_pid) +if [ -n "$pid" ] +then +echo "Geoserver is already running (pid: $pid)" +else + +echo "Starting Geoserver" +touch $CATALINA_PID +chown $USER $CATALINA_PID +/bin/su -p -s /bin/sh $USER $GEOSERVER_HOME/bin/startup.sh +echo "Started Geoserver with next variables:" +echo "GEOSERVER_HOME=$GEOSERVER_HOME" +echo "GEOSERVER_DATA_DIR=$GEOSERVER_DATA_DIR" +echo "Geoserver PID is: $(geoserver_pid)" +fi + +return 0 +} + +stop() { +pid=$(geoserver_pid) +if [ -n "$pid" ] +then +/bin/su -p -s /bin/sh $USER $GEOSERVER_HOME/bin/shutdown.sh +echo -n "Stopping Geoserver" + +let kwait=$SHUTDOWN_WAIT +count=0; +until [ `ps -p $pid | grep -c $pid` = '0' ] || [ $count -gt $kwait ] +do +echo -n "."; +sleep 1 +let count=$count+1; +done + +if [ $count -gt $kwait ]; then +echo "process is still running after $SHUTDOWN_WAIT seconds, killing process" +kill $pid +sleep 3 + +# if it’s still running use kill -9 +if [ `ps -p $pid | grep -c $pid` -gt '0' ]; then +echo "process is still running, using kill -9" +kill -9 $pid +sleep 3 +fi + +fi + +if [ `ps -p $pid | grep -c $pid` -gt '0' ]; then +echo "process is still running, I give up" +else +# success, delete PID file +rm $CATALINA_PID +fi +else +echo "Geoserver $INSTANCE is not running" +fi +return 0 +} + +case $1 in +start) +start +;; +stop) +stop +;; +restart) +stop +start +;; +status) +pid=$(geoserver_pid) +if [ -n "$pid" ] +then +echo "Geoserver $INSTANCE is running with pid: $pid" +else +echo "Geoserver $INSTANCE is not running" +fi +;; +esac +exit 0 \ No newline at end of file From fbe5acf87cfd33e56c2010e5ebce50323a2f8554 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 25 Aug 2012 12:13:01 +0200 Subject: [PATCH 48/72] [GEOS-5283] Bin package will OOM after a few demo requests --- src/release/bin/startup.bat | 1 + src/release/bin/startup.sh | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/release/bin/startup.bat b/src/release/bin/startup.bat index 8fb6fba0d1c..648bd48167a 100755 --- a/src/release/bin/startup.bat +++ b/src/release/bin/startup.bat @@ -113,6 +113,7 @@ goto run :run + if "%JAVA_OPTS%" == "" (set JAVA_OPTS=-XX:MaxPermSize=128m) set RUN_JAVA=%JAVA_HOME%\bin\java cd %GEOSERVER_HOME% echo Please wait while loading GeoServer... diff --git a/src/release/bin/startup.sh b/src/release/bin/startup.sh index fa2d18aa9da..9a58604fa06 100755 --- a/src/release/bin/startup.sh +++ b/src/release/bin/startup.sh @@ -61,6 +61,11 @@ if [ -z $GEOSERVER_DATA_DIR ]; then fi fi +# if not told otherwise pump up the permgen +if [ -z "$JAVA_OPS" ]; then + set JAVA_OPS=-XX:MaxPermSize=128m +fi + cd "$GEOSERVER_HOME" echo "GEOSERVER DATA DIR is $GEOSERVER_DATA_DIR" From d20130debe0eb65c3a525f7a2d9b526fab35413b Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 25 Aug 2012 12:19:08 +0200 Subject: [PATCH 49/72] Forgot a bit in the windows startup script --- src/release/bin/startup.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/release/bin/startup.bat b/src/release/bin/startup.bat index 648bd48167a..0469a2dbc65 100755 --- a/src/release/bin/startup.bat +++ b/src/release/bin/startup.bat @@ -118,7 +118,7 @@ goto run cd %GEOSERVER_HOME% echo Please wait while loading GeoServer... echo. - "%RUN_JAVA%" -DGEOSERVER_DATA_DIR="%GEOSERVER_DATA_DIR%" -Djava.awt.headless=true -DSTOP.PORT=8079 -DSTOP.KEY=geoserver -jar start.jar + "%RUN_JAVA%" "%JAVA_OPTS%" -DGEOSERVER_DATA_DIR="%GEOSERVER_DATA_DIR%" -Djava.awt.headless=true -DSTOP.PORT=8079 -DSTOP.KEY=geoserver -jar start.jar cd bin goto end From f1ffb32ae20de3b5ee2933d2f0a01f29eceadcbe Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 25 Aug 2012 12:39:18 +0200 Subject: [PATCH 50/72] [GEOS-5286] Tasmania cities preview fails due to empty bbox --- .../topp/taz_shapes/tasmania_cities/featuretype.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/release/workspaces/topp/taz_shapes/tasmania_cities/featuretype.xml b/data/release/workspaces/topp/taz_shapes/tasmania_cities/featuretype.xml index 3591e786d60..9953154cd26 100644 --- a/data/release/workspaces/topp/taz_shapes/tasmania_cities/featuretype.xml +++ b/data/release/workspaces/topp/taz_shapes/tasmania_cities/featuretype.xml @@ -20,10 +20,10 @@ AXIS["Latitude", NORTH]] EPSG:4326 - 147.2910004483 - 147.2910004483 - -42.851001816890005 - -42.851001816890005 + 145.19754 + 148.27298000000002 + -43.423512 + -40.852802 EPSG:4326 @@ -85,4 +85,4 @@ 0 0 -
          \ No newline at end of file + From 6c46849032ae3332e3be9c27938690ede23b1f04 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sat, 25 Aug 2012 14:57:10 +0200 Subject: [PATCH 51/72] Enhancing security documentation for role services --- .../security/usergrouprole/roleservices.rst | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/doc/en/user/source/security/usergrouprole/roleservices.rst b/doc/en/user/source/security/usergrouprole/roleservices.rst index fcef113de4e..3f490005869 100644 --- a/doc/en/user/source/security/usergrouprole/roleservices.rst +++ b/doc/en/user/source/security/usergrouprole/roleservices.rst @@ -7,6 +7,8 @@ A **role service** is a source of information for roles. It provides the followi * List of roles * Calculation of role assignments for a given user +* Mapping of a role to the system role ``ROLE_ADMINISTRATOR`` +* Mapping of a role to the system role ``ROLE_GROUP_ADMIN`` When a user/group service loads information about a user or a group, it delegates to the role service to determine which roles should be assigned to the user or group. Unlike :ref:`sec_rolesystem_usergroupservices`, there is only a single role service active at any given time. @@ -17,6 +19,29 @@ GeoServer comes by default with support for two types of role services: * JDBC - Role service persisted in a database via JDBC +.. _sec_rolesystem_mapping: + +Mapping roles to system roles +----------------------------- + +For assigning the system role ``ROLE_ADMINISTRATOR`` to a user or to a group, it is necessary to add a role with a different name and to map this role to ``ROLE_ADMINISTRATOR``. The same holds true for the system role ``ROLE_GROUP_ADMIN``. The mapping is stored in the ``config.xml`` file of the service. + +.. code-block:: xml + + + 471ed59f:13915c479bc:-7ffc + default + org.geoserver.security.xml.XMLRoleService + roles.xml + 10000 + true + ADMIN + GROUP_ADMIN + + +In this example, a user or a group having the role ``ADMIN`` has also the system role ``ROLE_ADMINISTRATOR``. Again, the same holds true for ``GROUP_ADMIN`` and ``ROLE_GROUP_ADMIN``. + + .. _sec_rolesystem_rolexml: XML role service @@ -28,23 +53,26 @@ This service represents the user database as XML corresponding to this :download named :file:`roles.xml` and is located inside the GeoServer data directory at a path of ``security/role//roles.xml``, where ```` is the name of the role service. +The service is configured to map the local role ``ADMIN`` to the system role ``ROLE_ADMINISTRATOR``. Additionally, ``GROUP_ADMIN`` is mapped to ``ROLE_GROUP_ADMIN``. The mapping is stored the ``config.xml`` of each role service, + The following is the contents of ``roles.xml`` that ships with the default GeoServer configuration: .. code-block:: xml - + + - + -This configuration contains a single role named ``ROLE_ADMINISTRATOR`` and assigns the role to the ``admin`` user. +This configuration contains two roles named ``ADMIN`` and ``GROUP_ADMIN``. The role ``ADMIN`` is assigned to the ``admin`` user. Since the ``ADMIN`` role is mapped to the system role ``ROLE_ADMINISTRATOR`` the role calculation assigns both roles to the ``admin`` user. Read more on :ref:`configuring a role service ` in the :ref:`web_admin`. @@ -138,8 +166,8 @@ The default GeoServer security configuration would be represented with the follo * - name - parent - * - ``ROLE_ADMINISTRATOR`` - - ``NULL`` + * - *Empty* + - *Empty* .. list-table:: Table: role_props @@ -159,8 +187,8 @@ The default GeoServer security configuration would be represented with the follo * - username - rolename - * - ``admin`` - - ``ROLE_ADMINISTRATOR`` + * - *Empty* + - *Empty* .. list-table:: Table: group_roles :widths: 15 15 From 47fd31c53640a9a6f9bf4351d316b74e0467d997 Mon Sep 17 00:00:00 2001 From: Mark Paxton Date: Fri, 24 Aug 2012 13:26:23 +0100 Subject: [PATCH 52/72] Adding a filter to provide open entity manager in view support for JPA persistance setups, equivalent to the open session in view filter for hibernate. --- .../geoserver/monitor/OpenEntityManagerInViewFilter.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java new file mode 100644 index 00000000000..fd51d52a7ec --- /dev/null +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java @@ -0,0 +1,8 @@ +package org.geoserver.monitor; + +import org.geoserver.filters.GeoServerFilter; + +public class OpenEntityManagerInViewFilter extends + org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter implements GeoServerFilter { + +} From 84f33ca9e5c732628b0cb8eaaa3b212194d2ea6a Mon Sep 17 00:00:00 2001 From: Mark Paxton Date: Fri, 24 Aug 2012 13:32:32 +0100 Subject: [PATCH 53/72] Added comments to OpenEntityManagerInViewFilter --- .../monitor/OpenEntityManagerInViewFilter.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java index fd51d52a7ec..f21040d2ffa 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/OpenEntityManagerInViewFilter.java @@ -1,7 +1,15 @@ +/* Copyright (c) 2001 - 2011 TOPP - www.openplans.org. All rights reserved. + * This code is licensed under the GPL 2.0 license, availible at the root + * application directory. + */ + package org.geoserver.monitor; import org.geoserver.filters.GeoServerFilter; - +/** + * Makes the JPA {@link OpenEntityManagerInViewFilter} be picked up by the {@link SpringDelegatingFilter} + * @author Mark Paxton + */ public class OpenEntityManagerInViewFilter extends org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter implements GeoServerFilter { From b1708b616d937d7fec0f85cc0bdad7e966a6b583 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Mon, 27 Aug 2012 09:01:19 +0200 Subject: [PATCH 54/72] Updating master password documentation --- doc/en/user/source/security/passwd.rst | 8 ++++++-- doc/en/user/source/webadmin/security/passwords.rst | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/en/user/source/security/passwd.rst b/doc/en/user/source/security/passwd.rst index 33952e19a78..dc7fef6eff6 100644 --- a/doc/en/user/source/security/passwd.rst +++ b/doc/en/user/source/security/passwd.rst @@ -101,8 +101,12 @@ GeoServer contains the ability to set a **master password** that serves two purp * Protect access to the :ref:`keystore ` * Protect access to the GeoServer :ref:`sec_root` -By default, the master password is set to ``geoserver``, though for obvious reasons it is strongly recommended that the master password be -changed **immediately** following any GeoServer installation. +By default, the master password is generated and stored in a file named ``security/masterpw.info`` using plain text. In case of an upgrade from an existing GeoServer data directory (versions 2.1.x and lower), the algorithm tries to figure out a password of a user having the role ``ROLE_ADMINISTRATOR``. If such a password is found and the password length is 8 characters at minimum, GeoServer uses this password as master password. Again, the name of the chosen user is found in ``security/masterpw.info``. + +.. warning:: The file ``security/masterpw.info`` is a security risk. The administrator should read this file and verify the master password doing a GeoServer login as ``root`` user. On success, this file should be removed. + +The master password can be changed as described in :ref:`webadmin_sec_masterpasswordprovider`. + .. warning:: JUSTIN-TODO: EXPLAIN MASTER PASSWORD PROVIDER diff --git a/doc/en/user/source/webadmin/security/passwords.rst b/doc/en/user/source/webadmin/security/passwords.rst index 6e8b6c6baad..d1045ec944a 100644 --- a/doc/en/user/source/webadmin/security/passwords.rst +++ b/doc/en/user/source/webadmin/security/passwords.rst @@ -7,6 +7,8 @@ This page sets the various options related to :ref:`sec_passwd`, the :ref:`sec_m .. note:: Passwords for users are changed in the Users dialogs accessed from the :ref:`webadmin_sec_ugr` page. +.. _webadmin_sec_masterpasswordprovider: + Active master password provider ------------------------------- From 78b131ac55709b001b071a75230fede1444cc849 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 25 Aug 2012 16:41:49 +0200 Subject: [PATCH 55/72] [GEOS-5287] KML with superoverlays generates empty documents --- .../org/geoserver/kml/KMLSuperOverlayTransformer.java | 9 +++++++++ .../geoserver/kml/KMLSuperOverlayTransformerTest.java | 9 ++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/wms/src/main/java/org/geoserver/kml/KMLSuperOverlayTransformer.java b/src/wms/src/main/java/org/geoserver/kml/KMLSuperOverlayTransformer.java index 827d364dd14..d983b65635e 100644 --- a/src/wms/src/main/java/org/geoserver/kml/KMLSuperOverlayTransformer.java +++ b/src/wms/src/main/java/org/geoserver/kml/KMLSuperOverlayTransformer.java @@ -382,6 +382,15 @@ void encodeRegion(Envelope box, int minLodPixels, int maxLodPixels) { } void encodeNetworkLink(Envelope box, String name, Layer layer) { + // check if we are going to get any feature from this layer + try { + if(!shouldDrawVectorLayer(layer, box)) { + return; + } + } catch(HttpErrorCodeException e ) { + // fine, it means there was no data.... sigh... + return; + } start("NetworkLink"); element("name", name); diff --git a/src/wms/src/test/java/org/geoserver/kml/KMLSuperOverlayTransformerTest.java b/src/wms/src/test/java/org/geoserver/kml/KMLSuperOverlayTransformerTest.java index faaeb38160a..d9414bbe85f 100644 --- a/src/wms/src/test/java/org/geoserver/kml/KMLSuperOverlayTransformerTest.java +++ b/src/wms/src/test/java/org/geoserver/kml/KMLSuperOverlayTransformerTest.java @@ -87,7 +87,8 @@ public void testWorldBoundsSuperOverlay() throws Exception { } /** - * Verify that when a tile smaller than one hemisphere is requested, four subtiles are included in the result. + * Verify that when a tile smaller than one hemisphere is requested, then subtiles are included + * in the result (but only the ones necessary for the data at hand) */ public void testSubtileSuperOverlay() throws Exception { KMLSuperOverlayTransformer transformer = new KMLSuperOverlayTransformer(getWMS(), @@ -102,10 +103,12 @@ public void testSubtileSuperOverlay() throws Exception { DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document document = docBuilder.parse(new ByteArrayInputStream(output.toByteArray())); + print(document); assertEquals("kml", document.getDocumentElement().getNodeName()); - assertEquals(6, document.getElementsByTagName("Region").getLength()); - assertEquals(5, document.getElementsByTagName("NetworkLink").getLength()); + // only two regions, the root one and one network link (that's all we need) + assertEquals(2, document.getElementsByTagName("Region").getLength()); + assertEquals(1, document.getElementsByTagName("NetworkLink").getLength()); assertEquals(0, document.getElementsByTagName("GroundOverlay").getLength()); } From 378338f5fa833fec231702c07820cb990232673f Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 25 Aug 2012 15:45:09 +0200 Subject: [PATCH 56/72] [GEOS-5284] wfsTransactioninsert demo request fails --- .../main/java/org/geoserver/wfs/xml/WFSURIHandler.java | 9 +++++++-- .../java/org/geoserver/wfs/xml/v1_0_0/WfsXmlReader.java | 2 +- .../java/org/geoserver/wfs/xml/v1_1_0/WfsXmlReader.java | 2 ++ .../java/org/geoserver/wfs/xml/v2_0/WfsXmlReader.java | 6 +++--- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/wfs/src/main/java/org/geoserver/wfs/xml/WFSURIHandler.java b/src/wfs/src/main/java/org/geoserver/wfs/xml/WFSURIHandler.java index d892a00417b..2c65cadf460 100644 --- a/src/wfs/src/main/java/org/geoserver/wfs/xml/WFSURIHandler.java +++ b/src/wfs/src/main/java/org/geoserver/wfs/xml/WFSURIHandler.java @@ -30,6 +30,7 @@ import org.geoserver.platform.Service; import org.geoserver.wfs.DescribeFeatureType; import org.geoserver.wfs.WFSInfo; +import org.geoserver.wfs.WFSInfo.Version; import org.geoserver.wfs.kvp.DescribeFeatureTypeKvpRequestReader; import org.geoserver.wfs.request.DescribeFeatureTypeRequest; import org.geoserver.wfs.xml.v1_1_0.XmlSchemaEncoder; @@ -93,7 +94,8 @@ public boolean canHandle(URI uri) { if (q != null && !"".equals(q.trim())) { KvpMap kv = parseQueryString(q); - if ("DescribeFeatureType".equalsIgnoreCase((String)kv.get("REQUEST"))) { + if ("DescribeFeatureType".equalsIgnoreCase((String)kv.get("REQUEST")) || + (uri.path().endsWith("DescribeFeatureType"))) { return true; } } @@ -140,7 +142,10 @@ public InputStream createInputStream(URI uri, Map options) throws IOExcept KvpMap kv = parseQueryString(uri.query()); //dispatch the correct describe feature type reader - WFSInfo.Version ver = WFSInfo.Version.negotiate((String)kv.get("VERSION")); + WFSInfo.Version ver = WFSInfo.Version.negotiate((String)kv.get("VERSION")); + if(ver == null) { + ver = WFSInfo.Version.latest(); + } DescribeFeatureTypeKvpRequestReader dftReqReader = null; switch(ver) { case V_10: diff --git a/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_0_0/WfsXmlReader.java b/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_0_0/WfsXmlReader.java index 3f718e4f93b..c237a271824 100644 --- a/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_0_0/WfsXmlReader.java +++ b/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_0_0/WfsXmlReader.java @@ -72,7 +72,7 @@ public Object read(Object request, Reader reader, Map kvp) throws Exception { } //set validation based on strict or not parser.setValidating(strict.booleanValue()); - parser.getURIHandlers().add(new WFSURIHandler(geoServer)); + parser.getURIHandlers().add(0, new WFSURIHandler(geoServer)); //parse Object parsed = parser.parse(reader); diff --git a/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_1_0/WfsXmlReader.java b/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_1_0/WfsXmlReader.java index e05c09dcb4d..0c2c2937879 100644 --- a/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_1_0/WfsXmlReader.java +++ b/src/wfs/src/main/java/org/geoserver/wfs/xml/v1_1_0/WfsXmlReader.java @@ -12,6 +12,7 @@ import org.geoserver.config.GeoServer; import org.geoserver.ows.XmlRequestReader; import org.geoserver.wfs.WFSInfo; +import org.geoserver.wfs.xml.WFSURIHandler; import org.geoserver.wfs.xml.WFSXmlUtils; import org.geotools.util.Version; import org.geotools.xml.Configuration; @@ -60,6 +61,7 @@ public Object read(Object request, Reader reader, Map kvp) throws Exception { WFSXmlUtils.initRequestParser(parser, wfs, geoServer, kvp); Object parsed = WFSXmlUtils.parseRequest(parser, reader, wfs); + parser.getURIHandlers().add(0, new WFSURIHandler(geoServer)); WFSXmlUtils.checkValidationErrors(parser, this); diff --git a/src/wfs/src/main/java/org/geoserver/wfs/xml/v2_0/WfsXmlReader.java b/src/wfs/src/main/java/org/geoserver/wfs/xml/v2_0/WfsXmlReader.java index 1b118cca169..3c16d7d2298 100644 --- a/src/wfs/src/main/java/org/geoserver/wfs/xml/v2_0/WfsXmlReader.java +++ b/src/wfs/src/main/java/org/geoserver/wfs/xml/v2_0/WfsXmlReader.java @@ -15,6 +15,7 @@ import org.geoserver.wfs.WFSException; import org.geoserver.wfs.WFSInfo; import org.geoserver.wfs.xml.FeatureTypeSchemaBuilder; +import org.geoserver.wfs.xml.WFSURIHandler; import org.geoserver.wfs.xml.WFSXmlUtils; import org.geotools.util.Version; import org.geotools.wfs.v2_0.WFS; @@ -22,7 +23,7 @@ import org.geotools.xml.Parser; /** - * Xml reader for wfs 1.0 xml requests. + * Xml reader for wfs 2.0 xml requests. * * @author Justin Deoliveira, OpenGeo * @@ -42,8 +43,7 @@ public Object read(Object request, Reader reader, Map kvp) throws Exception { WFSXmlUtils.initWfsConfiguration(config, gs, new FeatureTypeSchemaBuilder.GML32(gs)); Parser parser = new Parser(config); - - + parser.getURIHandlers().add(0, new WFSURIHandler(gs)); WFSInfo wfs = wfs(); From a37c1a058978e9736fc972f686751860e39a43c1 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Aug 2012 14:34:01 -0700 Subject: [PATCH 57/72] Added HTTP Referer field. --- .../org/geoserver/monitor/MonitorFilter.java | 13 ++++++ .../org/geoserver/monitor/RequestData.java | 14 +++++++ .../src/main/resources/mappings.hbm.xml | 2 + .../geoserver/monitor/MonitorFilterTest.java | 40 +++++++++++++++++-- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java index 9e7c5b15001..3a3f8f76605 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java @@ -110,6 +110,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha data.setInternalHost(InternalHostname.get()); data.setRemoteAddr(getRemoteAddr(req)); data.setStatus(Status.RUNNING); + data.setReferer(getReferer(req)); + if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) { @@ -188,6 +190,17 @@ String getRemoteAddr(HttpServletRequest req) { } } + String getReferer(HttpServletRequest req) { + String referer = req.getHeader("Referer"); + + // "Referer" is in the HTTP spec, but "Referrer" is the correct English spelling. + // This falls back to the "correct" spelling if the specified one was not used. + if(referer==null) + referer = req.getHeader("Referrer"); + + return referer; + } + static class PostProcessTask implements Runnable { Monitor monitor; diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java index b1f20e95398..ca1d11cc8af 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java @@ -205,6 +205,11 @@ public static enum Category { * The response status */ int responseStatus; + + /** + * The Referer of the HTTP request, if any + */ + private String referer; public long getId() { return id; @@ -497,6 +502,7 @@ public RequestData clone() { clone.setErrorMessage(errorMessage); clone.setError(error); clone.setResponseStatus(responseStatus); + clone.setReferer(referer); return clone; } @@ -513,4 +519,12 @@ public int getResponseStatus() { public void setResponseStatus(int httpStatus) { this.responseStatus = httpStatus; } + + public String getReferer() { + return referer; + } + + public void setReferer(String referer){ + this.referer = referer; + } } diff --git a/src/community/monitoring/src/main/resources/mappings.hbm.xml b/src/community/monitoring/src/main/resources/mappings.hbm.xml index b0affe5348d..33324510fa1 100644 --- a/src/community/monitoring/src/main/resources/mappings.hbm.xml +++ b/src/community/monitoring/src/main/resources/mappings.hbm.xml @@ -51,6 +51,8 @@ + + diff --git a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java index ce1e6b27a17..f4594a153ab 100644 --- a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java +++ b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java @@ -27,7 +27,7 @@ public void testSimple() throws Exception { MockFilterChain chain = new MockFilterChain(); - HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null); + HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null, null); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); @@ -45,7 +45,7 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep }); - req = request("POST", "/bar/foo", "78.56.34.12", "baz"); + req = request("POST", "/bar/foo", "78.56.34.12", "baz", null); filter.doFilter(req, response(), chain); data = dao.getLast(); @@ -58,7 +58,38 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep } - MockHttpServletRequest request(String method, String path, String remoteAddr, String body ) { + public void testReferer() throws Exception { + DummyMonitorDAO dao = new DummyMonitorDAO(); + + MonitorFilter filter = new MonitorFilter(new Monitor(dao), new MonitorRequestFilter()); + + MockFilterChain chain = new MockFilterChain(); + + HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null, "http://testhost/testpath"); + filter.doFilter(req, response(), chain); + + RequestData data = dao.getLast(); + assertEquals("GET", data.getHttpMethod()); + assertEquals("/foo/bar", data.getPath()); + assertEquals("12.34.56.78", data.getRemoteAddr()); + assertEquals("http://testhost/testpath", data.getReferer()); + + // "Referrer" was misspelled in the HTTP spec, check if it works with the "correct" + // spelling. + req = request("POST", "/bar/foo", "78.56.34.12", null, null); + ((MockHttpServletRequest)req).setHeader("Referrer", "http://testhost/testpath"); + filter.doFilter(req, response(), chain); + + data = dao.getLast(); + assertEquals("POST", data.getHttpMethod()); + assertEquals("/bar/foo", data.getPath()); + assertEquals("78.56.34.12", data.getRemoteAddr()); + assertEquals("http://testhost/testpath", data.getReferer()); + + + } + + MockHttpServletRequest request(String method, String path, String remoteAddr, String body, String referer) { MockHttpServletRequest req = new MockHttpServletRequest(); req.setMethod(method); req.setServerName("localhost"); @@ -66,7 +97,8 @@ MockHttpServletRequest request(String method, String path, String remoteAddr, St req.setPathInfo(path.substring(path.indexOf('/', 1))); req.setRemoteAddr(remoteAddr); req.setBodyContent(body); - + if(referer!=null) + req.setHeader("Referer", referer); return req; } From 00235f6a5379ad7a6212a7c49cb83b2cbb6427af Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Aug 2012 15:30:51 -0700 Subject: [PATCH 58/72] Broke test cases for MonitoringFilterTest into smaller parts. --- .../geoserver/monitor/MonitorFilterTest.java | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java index f4594a153ab..c69350e2acc 100644 --- a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java +++ b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java @@ -12,6 +12,9 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; +import org.junit.After; +import org.junit.Before; + import junit.framework.TestCase; import com.mockrunner.mock.web.MockFilterChain; @@ -19,13 +22,26 @@ import com.mockrunner.mock.web.MockHttpServletResponse; public class MonitorFilterTest extends TestCase { - - public void testSimple() throws Exception { - DummyMonitorDAO dao = new DummyMonitorDAO(); + + DummyMonitorDAO dao; + MonitorFilter filter; + MockFilterChain chain; + + @Before + public void setUp() throws Exception { + dao = new DummyMonitorDAO(); - MonitorFilter filter = new MonitorFilter(new Monitor(dao), new MonitorRequestFilter()); + filter = new MonitorFilter(new Monitor(dao), new MonitorRequestFilter()); - MockFilterChain chain = new MockFilterChain(); + chain = new MockFilterChain(); + } + + @After + public void tearDown() throws Exception { + + } + + public void testSimple() throws Exception { HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null, null); filter.doFilter(req, response(), chain); @@ -34,7 +50,10 @@ public void testSimple() throws Exception { assertEquals("GET", data.getHttpMethod()); assertEquals("/foo/bar", data.getPath()); assertEquals("12.34.56.78", data.getRemoteAddr()); - + assertNull(data.getReferer()); + } + + public void testSimple2() throws Exception { chain.setServlet(new HttpServlet() { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, @@ -45,13 +64,14 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep }); - req = request("POST", "/bar/foo", "78.56.34.12", "baz", null); + HttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", "baz", null); filter.doFilter(req, response(), chain); - data = dao.getLast(); + RequestData data = dao.getLast(); assertEquals("POST", data.getHttpMethod()); assertEquals("/bar/foo", data.getPath()); assertEquals("78.56.34.12", data.getRemoteAddr()); + assertNull(data.getReferer()); assertEquals(new String(data.getBody()), "baz"); assertEquals(5, data.getResponseLength()); @@ -59,12 +79,6 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep } public void testReferer() throws Exception { - DummyMonitorDAO dao = new DummyMonitorDAO(); - - MonitorFilter filter = new MonitorFilter(new Monitor(dao), new MonitorRequestFilter()); - - MockFilterChain chain = new MockFilterChain(); - HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null, "http://testhost/testpath"); filter.doFilter(req, response(), chain); @@ -73,14 +87,16 @@ public void testReferer() throws Exception { assertEquals("/foo/bar", data.getPath()); assertEquals("12.34.56.78", data.getRemoteAddr()); assertEquals("http://testhost/testpath", data.getReferer()); - + + } + public void testReferrer() throws Exception { // "Referrer" was misspelled in the HTTP spec, check if it works with the "correct" // spelling. - req = request("POST", "/bar/foo", "78.56.34.12", null, null); + MockHttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", null, null); ((MockHttpServletRequest)req).setHeader("Referrer", "http://testhost/testpath"); filter.doFilter(req, response(), chain); - data = dao.getLast(); + RequestData data = dao.getLast(); assertEquals("POST", data.getHttpMethod()); assertEquals("/bar/foo", data.getPath()); assertEquals("78.56.34.12", data.getRemoteAddr()); From 9f8e43475048037898ffe31fcbd6d7f9e270550e Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 22 Aug 2012 12:07:02 -0700 Subject: [PATCH 59/72] Trim request bodies to max length of Hibernate persistance DB and fixed related lazy initialization bug and test case bug. --- .../org/geoserver/monitor/MonitorFilter.java | 21 +++++++- .../monitor/MonitorServletRequest.java | 22 ++++---- .../org/geoserver/monitor/RequestData.java | 6 +++ .../geoserver/monitor/MonitorFilterTest.java | 50 ++++++++++++++++++- 4 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java index 3a3f8f76605..f9c782d9cfd 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java @@ -9,6 +9,7 @@ import java.net.URLDecoder; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutorService; @@ -36,6 +37,8 @@ public class MonitorFilter implements GeoServerFilter { + static final int MAX_BODY_SIZE=1024; + static Logger LOGGER = Logging.getLogger("org.geoserver.monitor"); Monitor monitor; @@ -139,7 +142,10 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha } data = monitor.current(); - data.setBody(((MonitorServletRequest)request).getBodyContent()); + + request.getInputStream(); // FIXME: here for debugging purposes + + data.setBody(getBody((MonitorServletRequest) request)); data.setBodyContentLength(((MonitorServletRequest)request).getBytesRead()); data.setResponseContentType(response.getContentType()); data.setResponseLength(((MonitorServletResponse)response).getContentLength()); @@ -201,6 +207,19 @@ String getReferer(HttpServletRequest req) { return referer; } + // Get the body and trim to the maximum allowable size if necessary + byte[] getBody(HttpServletRequest req) { + try { + byte[] body=((MonitorServletRequest)req).getBodyContent(); + if(body!=null && body.length>MAX_BODY_SIZE) + body=Arrays.copyOfRange(body, 0, MAX_BODY_SIZE); + return body; + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Could not read request body", ex); + return null; + } + } + static class PostProcessTask implements Runnable { Monitor monitor; diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java index 368060bc4cf..cab3a4bce87 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java @@ -22,25 +22,25 @@ public MonitorServletRequest(HttpServletRequest request, long maxSize) { this.maxSize = maxSize; } - public byte[] getBodyContent() { - if (input == null) { - return null; - } - return input.getData(); + public byte[] getBodyContent() throws IOException { + MonitorInputStream stream = getInputStream(); + return stream.getData(); } - public long getBytesRead() { - if (input == null) { - return -1; + public long getBytesRead(){ + try { + MonitorInputStream stream = getInputStream(); + return stream.getBytesRead(); + } catch (IOException ex) { + return 0; } - - return input.getBytesRead(); } @Override public MonitorInputStream getInputStream() throws IOException { if (input == null) { - input = new MonitorInputStream(super.getInputStream(), maxSize); + ServletInputStream delegateTo = super.getInputStream(); + input = new MonitorInputStream(delegateTo, maxSize); } return input; } diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java index ca1d11cc8af..2cd0ec1dd6a 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java @@ -251,6 +251,12 @@ public void setQueryString(String queryString) { this.queryString = queryString; } + /** + * The body of the HTTP request + * + * May be trimmed to a maximum length. If so, check getBodyContentLength for the length of the + * untrimmed body. + */ public byte[] getBody() { return body; } diff --git a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java index c69350e2acc..595c55bb3ad 100644 --- a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java +++ b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java @@ -27,6 +27,9 @@ public class MonitorFilterTest extends TestCase { MonitorFilter filter; MockFilterChain chain; + static final int MAX_BODY_SIZE = 1024; + static final int LONG_BODY_SIZE = 2048; + @Before public void setUp() throws Exception { dao = new DummyMonitorDAO(); @@ -34,6 +37,15 @@ public void setUp() throws Exception { filter = new MonitorFilter(new Monitor(dao), new MonitorRequestFilter()); chain = new MockFilterChain(); + + chain.setServlet(new HttpServlet() { + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, + IOException { + req.getInputStream().read(new byte[LONG_BODY_SIZE]); + res.getOutputStream().write(new byte[0]); + } + }); } @After @@ -53,12 +65,12 @@ public void testSimple() throws Exception { assertNull(data.getReferer()); } - public void testSimple2() throws Exception { + public void testWithBody() throws Exception { chain.setServlet(new HttpServlet() { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { - req.getInputStream().read(new byte[10]); + req.getInputStream().read(new byte[LONG_BODY_SIZE]); res.getOutputStream().write("hello".getBytes()); } }); @@ -74,8 +86,39 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep assertNull(data.getReferer()); assertEquals(new String(data.getBody()), "baz"); + assertEquals(3, data.getBodyContentLength()); assertEquals(5, data.getResponseLength()); + } + public void testWithLongBody() throws Exception { + chain.setServlet(new HttpServlet() { + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, + IOException { + req.getInputStream().read(new byte[LONG_BODY_SIZE]); + res.getOutputStream().write("hello".getBytes()); + } + }); + + StringBuilder b = new StringBuilder(); + + for (int i=0; i Date: Wed, 22 Aug 2012 12:11:18 -0700 Subject: [PATCH 60/72] Updated docs for configuing DB for Monitoring extension. --- .../community/monitoring/configuration.rst | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/en/user/source/community/monitoring/configuration.rst b/doc/en/user/source/community/monitoring/configuration.rst index e765014a4c6..99a744367fc 100644 --- a/doc/en/user/source/community/monitoring/configuration.rst +++ b/doc/en/user/source/community/monitoring/configuration.rst @@ -69,18 +69,33 @@ Monitor Database By default monitored request data is stored in an embedded H2 database located in the ``monitoring`` directory. This can be changed by editing the -``db.properties`` file:: +``db.properties`` and ``hibernate.properties`` file:: # default configuration is for h2 driver=org.h2.Driver url=jdbc:h2:file:${GEOSERVER_DATA_DIR}/monitoring/monitoring -For example to store request data in an external PostgreSQL database:: +For example to store request data in an external PostgreSQL database, set ``db.properties`` to:: driver=org.postgresql.Driver url=jdbc:postgresql://192.168.1.124:5432/monitoring username=bob password=foobar + +and ``hibernate.properties`` to:: + + hibernate.use_sql_comments=true + databasePlatform=org.hibernate.dialect.PostgreSQLDialect + generateDdl=true + hibernate.format_sql=true + showSql=false + hibernate.generate_statistics=true + hibernate.session_factory_name=SessionFactory + hibernate.hbm2ddl.auto=update + hibernate.bytecode.use_reflection_optimizer=true + database=POSTGRESQL + hibernate.show_sql=false + Request Filters --------------- From 1ae04a1374072d9a872d7e4ab58152f2626748c2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 23 Aug 2012 09:27:37 -0700 Subject: [PATCH 61/72] Fixed HTTP Response code handling --- .../src/main/java/org/geoserver/monitor/RequestData.java | 6 +++--- .../monitoring/src/main/resources/mappings.hbm.xml | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java index 2cd0ec1dd6a..5d60e597f3e 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java @@ -204,7 +204,7 @@ public static enum Category { /** * The response status */ - int responseStatus; + Integer responseStatus; /** * The Referer of the HTTP request, if any @@ -518,11 +518,11 @@ public String toString() { return "Request (" + String.valueOf(id) + ")"; } - public int getResponseStatus() { + public Integer getResponseStatus() { return responseStatus; } - public void setResponseStatus(int httpStatus) { + public void setResponseStatus(Integer httpStatus) { this.responseStatus = httpStatus; } diff --git a/src/community/monitoring/src/main/resources/mappings.hbm.xml b/src/community/monitoring/src/main/resources/mappings.hbm.xml index 33324510fa1..c2db416a7c2 100644 --- a/src/community/monitoring/src/main/resources/mappings.hbm.xml +++ b/src/community/monitoring/src/main/resources/mappings.hbm.xml @@ -51,6 +51,8 @@ + + From 2833b544026d4231df94f9693038658653690a29 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 23 Aug 2012 10:29:52 -0700 Subject: [PATCH 62/72] Added CSV quoted text implementation. --- .../geoserver/monitor/rest/RequestResource.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java index 4cd554de2ee..101b2855df7 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java @@ -20,6 +20,8 @@ import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -379,17 +381,26 @@ public void visit(RequestData data, Object... aggregates) { void writeRequest(RequestData data, BufferedWriter w) throws IOException { StringBuffer sb = new StringBuffer(); + // If whitespace, CR, LF, double quote, or comma occur, quote and escape + Pattern escapeRequired = Pattern.compile("[\\,\r\\n\\s\"]"); for (String fld : fields) { Object val = OwsUtils.get(data, fld); if (val instanceof Date) { val = DateUtil.serializeDateTime((Date)val); } if (val != null) { - val = val.toString().replaceAll(",", " ").replaceAll("\n", " "); + //val = val.toString().replaceAll(",", " ").replaceAll("\n", " "); + String string = val.toString(); + Matcher match = escapeRequired.matcher(string); + if(match.find()){ + string=string.replaceAll("\"", "\"\"");// Double all double quotes to escape + string=String.format("\"%s\"", string);// wrap in double quotes + } + val=string; } sb.append(val).append(","); } - sb.setLength(sb.length()-1); + sb.setLength(sb.length()-1); // Remove trailing comma sb.append("\n"); w.write(sb.toString()); } From ee73809312b11bde48a7a626afefdb1f6b5b8f9f Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 23 Aug 2012 11:13:27 -0700 Subject: [PATCH 63/72] Updated documentation on Monitoring extension's reporting API. --- .../user/source/community/monitoring/http.rst | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/doc/en/user/source/community/monitoring/http.rst b/doc/en/user/source/community/monitoring/http.rst index f6e0f46f3b3..d3723ee3197 100644 --- a/doc/en/user/source/community/monitoring/http.rst +++ b/doc/en/user/source/community/monitoring/http.rst @@ -25,6 +25,21 @@ Request information can be returned in CSV format, for easier post-processing:: GET http://localhost:8080/geoserver/rest/monitor/requests.csv +Request bodies containing newlines are handled with quoted text. If your CSV reader doesn't handle quoted newlines, it will not work correctly. + +All requests as PKZip +^^^^^^^^^^^^^^^^^^^^^ +A PKZip archive containing the CSV file above, with all the request bodies and errors as separate files:: + + GET http://localhost:8080/geoserver/rest/monitor/requests.zip + +All requests as MS Excel +^^^^^^^^^^^^^^^^^^^^^^^^ +A Microsoft Excel spreadsheet containing the same information as the CSV file:: + + GET http://localhost:8080/geoserver/rest/monitor/requests.xls + + Requests during a time period ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Requests can be filtered by date and time range:: @@ -66,6 +81,8 @@ and ``format`` specifies the representation of the returned result as one of: * ``html`` - an HTML table. * ``csv`` - a Comma Separated Values table. +* ``zip`` - PKZip archive containing CSV as above, plus plain text of errors and request body. +* ``xls`` - Microsoft Excel spreadsheet. .. note:: @@ -75,6 +92,8 @@ and ``format`` specifies the representation of the returned result as one of: * ``text/html`` * ``application/csv`` + * ``application/zip`` + * ``application/vnd.ms-excel`` See the `HTTP specification `_ for more information about the ``Accept`` header. @@ -187,9 +206,9 @@ Specifies which request attribute to sort by, and optionally specifies the sort * - ``order=[;]`` - requests.html?order=path * - - - requests.html?order=startTime:DESC + - requests.html?order=startTime;DESC * - - - requests.html?order=totalTime:ASC + - requests.html?order=totalTime;ASC From fc5d188394dceafc0e2b28d8f737b6e84cf1b55f Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 24 Aug 2012 11:12:43 -0700 Subject: [PATCH 64/72] Changed referer property to httpReferer. --- .../java/org/geoserver/monitor/MonitorFilter.java | 4 ++-- .../main/java/org/geoserver/monitor/RequestData.java | 12 ++++++------ .../monitoring/src/main/resources/mappings.hbm.xml | 2 +- .../org/geoserver/monitor/MonitorFilterTest.java | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java index f9c782d9cfd..6150cd502c2 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java @@ -113,7 +113,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha data.setInternalHost(InternalHostname.get()); data.setRemoteAddr(getRemoteAddr(req)); data.setStatus(Status.RUNNING); - data.setReferer(getReferer(req)); + data.setHttpReferer(getHttpReferer(req)); if (SecurityContextHolder.getContext() != null @@ -196,7 +196,7 @@ String getRemoteAddr(HttpServletRequest req) { } } - String getReferer(HttpServletRequest req) { + String getHttpReferer(HttpServletRequest req) { String referer = req.getHeader("Referer"); // "Referer" is in the HTTP spec, but "Referrer" is the correct English spelling. diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java index 5d60e597f3e..ebd23562f46 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/RequestData.java @@ -209,7 +209,7 @@ public static enum Category { /** * The Referer of the HTTP request, if any */ - private String referer; + private String httpReferer; public long getId() { return id; @@ -508,7 +508,7 @@ public RequestData clone() { clone.setErrorMessage(errorMessage); clone.setError(error); clone.setResponseStatus(responseStatus); - clone.setReferer(referer); + clone.setHttpReferer(httpReferer); return clone; } @@ -526,11 +526,11 @@ public void setResponseStatus(Integer httpStatus) { this.responseStatus = httpStatus; } - public String getReferer() { - return referer; + public String getHttpReferer() { + return httpReferer; } - public void setReferer(String referer){ - this.referer = referer; + public void setHttpReferer(String httpReferer){ + this.httpReferer = httpReferer; } } diff --git a/src/community/monitoring/src/main/resources/mappings.hbm.xml b/src/community/monitoring/src/main/resources/mappings.hbm.xml index c2db416a7c2..3f2ac9ee516 100644 --- a/src/community/monitoring/src/main/resources/mappings.hbm.xml +++ b/src/community/monitoring/src/main/resources/mappings.hbm.xml @@ -53,7 +53,7 @@ - + diff --git a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java index 595c55bb3ad..1a4d6fddde3 100644 --- a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java +++ b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java @@ -62,7 +62,7 @@ public void testSimple() throws Exception { assertEquals("GET", data.getHttpMethod()); assertEquals("/foo/bar", data.getPath()); assertEquals("12.34.56.78", data.getRemoteAddr()); - assertNull(data.getReferer()); + assertNull(data.getHttpReferer()); } public void testWithBody() throws Exception { @@ -83,7 +83,7 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep assertEquals("POST", data.getHttpMethod()); assertEquals("/bar/foo", data.getPath()); assertEquals("78.56.34.12", data.getRemoteAddr()); - assertNull(data.getReferer()); + assertNull(data.getHttpReferer()); assertEquals(new String(data.getBody()), "baz"); assertEquals(3, data.getBodyContentLength()); @@ -129,7 +129,7 @@ public void testReferer() throws Exception { assertEquals("GET", data.getHttpMethod()); assertEquals("/foo/bar", data.getPath()); assertEquals("12.34.56.78", data.getRemoteAddr()); - assertEquals("http://testhost/testpath", data.getReferer()); + assertEquals("http://testhost/testpath", data.getHttpReferer()); } public void testReferrer() throws Exception { @@ -143,7 +143,7 @@ public void testReferrer() throws Exception { assertEquals("POST", data.getHttpMethod()); assertEquals("/bar/foo", data.getPath()); assertEquals("78.56.34.12", data.getRemoteAddr()); - assertEquals("http://testhost/testpath", data.getReferer()); + assertEquals("http://testhost/testpath", data.getHttpReferer()); } From b23541f7602be6d4d55d4c98f00808f08d82f141 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 24 Aug 2012 15:16:51 -0700 Subject: [PATCH 65/72] Made maximum body length configurable and documented configuration option. --- .../community/monitoring/configuration.rst | 1 + .../org/geoserver/monitor/MonitorConfig.java | 3 +- .../org/geoserver/monitor/MonitorFilter.java | 9 ++-- .../monitor/MonitorServletRequest.java | 8 +++- .../src/main/resources/mappings.hbm.xml | 4 ++ .../org/geoserver/monitor/monitor.properties | 10 +++++ .../geoserver/monitor/MonitorFilterTest.java | 41 +++++++++++++++++-- 7 files changed, 66 insertions(+), 10 deletions(-) diff --git a/doc/en/user/source/community/monitoring/configuration.rst b/doc/en/user/source/community/monitoring/configuration.rst index 99a744367fc..5e03c2d8e91 100644 --- a/doc/en/user/source/community/monitoring/configuration.rst +++ b/doc/en/user/source/community/monitoring/configuration.rst @@ -96,6 +96,7 @@ and ``hibernate.properties`` to:: database=POSTGRESQL hibernate.show_sql=false +The maximum size of the request body that is logged is set in the ``monitor.properties`` and ``mappings.hbm.xml`` files. If the setting in ``monitor.properties`` is higher than that in ``mappings.hbm.xml`` any long requests will fail to be logged entirely. You can set it to be unbounded if your database supports it. Request Filters --------------- diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorConfig.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorConfig.java index b4f515fc482..ed859454f1d 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorConfig.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorConfig.java @@ -44,6 +44,7 @@ public MonitorConfig() { props = new Properties(); props.put("mode", "history"); props.put("sync", "async"); + props.put("maxBodySize", "1024"); } public MonitorConfig(GeoServerResourceLoader loader) throws IOException { @@ -65,7 +66,7 @@ public Sync getSync() { } public long getMaxBodySize() { - return Long.parseLong(props.getProperty("maxBodySize", String.valueOf(8 * 1024))); + return Long.parseLong(props.getProperty("maxBodySize", String.valueOf(1024))); } public boolean isEnabled() { diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java index 6150cd502c2..e0378fd53ad 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorFilter.java @@ -37,7 +37,6 @@ public class MonitorFilter implements GeoServerFilter { - static final int MAX_BODY_SIZE=1024; static Logger LOGGER = Logging.getLogger("org.geoserver.monitor"); @@ -143,7 +142,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha data = monitor.current(); - request.getInputStream(); // FIXME: here for debugging purposes data.setBody(getBody((MonitorServletRequest) request)); data.setBodyContentLength(((MonitorServletRequest)request).getBytesRead()); @@ -209,10 +207,11 @@ String getHttpReferer(HttpServletRequest req) { // Get the body and trim to the maximum allowable size if necessary byte[] getBody(HttpServletRequest req) { + long maxBodyLength = monitor.config.getMaxBodySize(); try { - byte[] body=((MonitorServletRequest)req).getBodyContent(); - if(body!=null && body.length>MAX_BODY_SIZE) - body=Arrays.copyOfRange(body, 0, MAX_BODY_SIZE); + byte[] body=((MonitorServletRequest)req).getBodyContent(); // TODO: trimming at this point may now be redundant + if(body!=null && maxBodyLength!=MonitorServletRequest.BODY_SIZE_UNBOUNDED && body.length>maxBodyLength) + body=Arrays.copyOfRange(body, 0, (int) maxBodyLength); return body; } catch (IOException ex) { LOGGER.log(Level.WARNING, "Could not read request body", ex); diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java index cab3a4bce87..8437a7cd9dd 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/MonitorServletRequest.java @@ -13,6 +13,11 @@ public class MonitorServletRequest extends HttpServletRequestWrapper { + /** + * Don't restrict the maximum length of a request body. + */ + public static final long BODY_SIZE_UNBOUNDED = -1; + MonitorInputStream input; long maxSize; @@ -93,7 +98,8 @@ public int read() throws IOException { buffer.write((byte) b); } - nbytes += 1; + + if(b>=0) nbytes += 1; // Increment byte count unless EoF marker return b; } diff --git a/src/community/monitoring/src/main/resources/mappings.hbm.xml b/src/community/monitoring/src/main/resources/mappings.hbm.xml index 3f2ac9ee516..c91b0ea9647 100644 --- a/src/community/monitoring/src/main/resources/mappings.hbm.xml +++ b/src/community/monitoring/src/main/resources/mappings.hbm.xml @@ -20,7 +20,11 @@ + + + + diff --git a/src/community/monitoring/src/main/resources/org/geoserver/monitor/monitor.properties b/src/community/monitoring/src/main/resources/org/geoserver/monitor/monitor.properties index d5056887b29..fc79d044ec3 100644 --- a/src/community/monitoring/src/main/resources/org/geoserver/monitor/monitor.properties +++ b/src/community/monitoring/src/main/resources/org/geoserver/monitor/monitor.properties @@ -6,3 +6,13 @@ mode=history # WARNING: this is an advanced configuration option. You probably do not want # to change this unless instructed to by a developer sync=async + +# The maximum allowable length for a request body (in bytes). Longer bodies will be trimmed to +# this length. +maxBodySize=1024 + +# Allow unlimited body lengths. This could take up a lot of space quite rapidly. +# maxBodySize=-1 + +# If you increase or unbound the maximum body length, you must also change the hibernate mappings +# file. \ No newline at end of file diff --git a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java index 1a4d6fddde3..3147539c156 100644 --- a/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java +++ b/src/community/monitoring/src/test/java/org/geoserver/monitor/MonitorFilterTest.java @@ -27,8 +27,8 @@ public class MonitorFilterTest extends TestCase { MonitorFilter filter; MockFilterChain chain; - static final int MAX_BODY_SIZE = 1024; - static final int LONG_BODY_SIZE = 2048; + static final int MAX_BODY_SIZE = 10; + static final int LONG_BODY_SIZE = 3*MAX_BODY_SIZE; @Before public void setUp() throws Exception { @@ -46,6 +46,8 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep res.getOutputStream().write(new byte[0]); } }); + + filter.monitor.config.props.put("maxBodySize", Integer.toString(MAX_BODY_SIZE)); // Ensure the configured property is correct for the tests } @After @@ -120,7 +122,40 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep assertEquals(LONG_BODY_SIZE, data.getBodyContentLength()); // Should be the full length, not the trimmed one } - + public void testWithUnboundedBody() throws Exception { + final int UNBOUNDED_BODY_SIZE= 10000; // Something really big + + filter.monitor.config.props.put("maxBodySize", Integer.toString(UNBOUNDED_BODY_SIZE)); // Ensure the configured property is correct for the tests + + chain.setServlet(new HttpServlet() { + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, + IOException { + while(req.getInputStream().read()!=-1); // "read" the stream until the end. + req.getInputStream().read(); + res.getOutputStream().write("hello".getBytes()); + } + }); + + StringBuilder b = new StringBuilder(); + + for (int i=0; i Date: Mon, 27 Aug 2012 12:09:24 -0700 Subject: [PATCH 66/72] Optimizations to CSV output. --- .../monitor/rest/RequestResource.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java b/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java index 101b2855df7..e6ef4f285b3 100644 --- a/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java +++ b/src/community/monitoring/src/main/java/org/geoserver/monitor/rest/RequestResource.java @@ -346,6 +346,10 @@ static class CSVFormat extends StreamDataFormat { Monitor monitor; String[] fields; + + // Regexp matching problematic characters which trigger quoted text mode. + static Pattern escapeRequired = Pattern.compile("[\\,\\s\"]"); + protected CSVFormat(String[] fields, Monitor monitor) { super(new MediaType("application/csv")); @@ -381,24 +385,26 @@ public void visit(RequestData data, Object... aggregates) { void writeRequest(RequestData data, BufferedWriter w) throws IOException { StringBuffer sb = new StringBuffer(); - // If whitespace, CR, LF, double quote, or comma occur, quote and escape - Pattern escapeRequired = Pattern.compile("[\\,\r\\n\\s\"]"); + for (String fld : fields) { Object val = OwsUtils.get(data, fld); if (val instanceof Date) { val = DateUtil.serializeDateTime((Date)val); } if (val != null) { - //val = val.toString().replaceAll(",", " ").replaceAll("\n", " "); String string = val.toString(); Matcher match = escapeRequired.matcher(string); - if(match.find()){ + if(match.find()){ // may need escaping, so escape string=string.replaceAll("\"", "\"\"");// Double all double quotes to escape - string=String.format("\"%s\"", string);// wrap in double quotes + sb.append("\""); + sb.append(string); + sb.append("\""); + } + else { // No need for escaping + sb.append(string); } - val=string; } - sb.append(val).append(","); + sb.append(","); } sb.setLength(sb.length()-1); // Remove trailing comma sb.append("\n"); From a46b206ce4ace7c8aa7fcd7a06bda9df63c65433 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 28 Aug 2012 14:04:07 -0700 Subject: [PATCH 67/72] Upped size of HTTP_REFERER field in Hibernate mappings from default 255 to 4096 --- src/community/monitoring/src/main/resources/mappings.hbm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/community/monitoring/src/main/resources/mappings.hbm.xml b/src/community/monitoring/src/main/resources/mappings.hbm.xml index c91b0ea9647..11d30f39f6e 100644 --- a/src/community/monitoring/src/main/resources/mappings.hbm.xml +++ b/src/community/monitoring/src/main/resources/mappings.hbm.xml @@ -57,7 +57,7 @@ - + From f66e3bc8a2f4d73816fce664913ff67ce39e26a4 Mon Sep 17 00:00:00 2001 From: Robert Marianski Date: Wed, 29 Aug 2012 10:26:01 -0400 Subject: [PATCH 68/72] update link to to download optional extensions. Backport of pull request #22 from Rob Marianski. --- doc/en/user/source/production/java.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/user/source/production/java.rst b/doc/en/user/source/production/java.rst index af090212532..3194071c3c1 100644 --- a/doc/en/user/source/production/java.rst +++ b/doc/en/user/source/production/java.rst @@ -71,7 +71,7 @@ Installing native JAI manually You can install the native JAI manually if you encounter problems using the above installers, or if you wish to install the native JAI for more than one JDK/JRE. -Please refer to the `GeoTools page on JAI installation `_ for details. +Please refer to the `GeoTools page on JAI installation `_ for details. GeoServer cleanup From 29d6d02e05e39e21e40860d40d22f4e69a21951b Mon Sep 17 00:00:00 2001 From: Daniele Romagnoli Date: Fri, 31 Aug 2012 14:05:47 +0200 Subject: [PATCH 69/72] GEOS-5292: fixing sldService --- .../rest/finder/ClassifierResourceFinder.java | 13 ++++++--- .../rest/finder/LayerAttributesFinder.java | 27 ++++++++++++------- .../rest/finder/RasterizerResourceFinder.java | 14 +++++++--- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/ClassifierResourceFinder.java b/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/ClassifierResourceFinder.java index b052f1c4dfd..abf3ad6fdc8 100644 --- a/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/ClassifierResourceFinder.java +++ b/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/ClassifierResourceFinder.java @@ -20,18 +20,23 @@ package org.geoserver.sldservice.rest.finder; import org.geoserver.catalog.Catalog; -import org.geoserver.catalog.rest.AbstractCatalogFinder; import org.geoserver.rest.RestletException; import org.geoserver.sldservice.rest.resource.ClassifierResource; +import org.restlet.Finder; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.data.Status; import org.restlet.resource.Resource; -public class ClassifierResourceFinder extends AbstractCatalogFinder { +public class ClassifierResourceFinder extends Finder { - public ClassifierResourceFinder(Catalog catalog) { - super(catalog); + /** + * reference to the catalog + */ + protected Catalog catalog; + + protected ClassifierResourceFinder(Catalog catalog) { + this.catalog = catalog; } @Override diff --git a/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/LayerAttributesFinder.java b/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/LayerAttributesFinder.java index 7494df73879..b897aee1aff 100644 --- a/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/LayerAttributesFinder.java +++ b/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/LayerAttributesFinder.java @@ -20,30 +20,37 @@ package org.geoserver.sldservice.rest.finder; import org.geoserver.catalog.Catalog; -import org.geoserver.catalog.rest.AbstractCatalogFinder; import org.geoserver.rest.RestletException; import org.geoserver.sldservice.rest.resource.ListAttributesResource; +import org.restlet.Finder; import org.restlet.data.Method; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.data.Status; import org.restlet.resource.Resource; -public class LayerAttributesFinder extends AbstractCatalogFinder { +public class LayerAttributesFinder extends Finder { - public LayerAttributesFinder(Catalog catalog) { - super(catalog); + /** + * reference to the catalog + */ + protected Catalog catalog; + + protected LayerAttributesFinder(Catalog catalog) { + this.catalog = catalog; } @Override public Resource findTarget(Request request, Response response) { - String layer = (String) request.getAttributes().get( "layer" ); - - if ( layer != null && request.getMethod() == Method.GET ) { - return new ListAttributesResource(getContext(),request,response,catalog); + String layer = (String) request.getAttributes().get("layer"); + + if (layer != null && request.getMethod() == Method.GET) { + return new ListAttributesResource(getContext(), request, response, + catalog); } - - throw new RestletException( "No such layer: " + layer, Status.CLIENT_ERROR_NOT_FOUND ); + + throw new RestletException("No such layer: " + layer, + Status.CLIENT_ERROR_NOT_FOUND); } } \ No newline at end of file diff --git a/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/RasterizerResourceFinder.java b/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/RasterizerResourceFinder.java index 13076c0a943..177f169957e 100644 --- a/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/RasterizerResourceFinder.java +++ b/src/community/sldService/src/main/java/org/geoserver/sldservice/rest/finder/RasterizerResourceFinder.java @@ -20,9 +20,9 @@ package org.geoserver.sldservice.rest.finder; import org.geoserver.catalog.Catalog; -import org.geoserver.catalog.rest.AbstractCatalogFinder; import org.geoserver.rest.RestletException; import org.geoserver.sldservice.rest.resource.RasterizerResource; +import org.restlet.Finder; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.data.Status; @@ -32,10 +32,16 @@ * @author Alessio * */ -public class RasterizerResourceFinder extends AbstractCatalogFinder { +public class RasterizerResourceFinder extends Finder { - public RasterizerResourceFinder(Catalog catalog) { - super(catalog); + + /** + * reference to the catalog + */ + protected Catalog catalog; + + protected RasterizerResourceFinder( Catalog catalog ) { + this.catalog = catalog; } @Override From e5ee8fb46829410d5b06ba297984758c6e11b11d Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 1 Sep 2012 17:10:07 +0200 Subject: [PATCH 70/72] Fixing typo in startup script --- src/release/bin/startup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/release/bin/startup.sh b/src/release/bin/startup.sh index 9a58604fa06..511cee48d38 100755 --- a/src/release/bin/startup.sh +++ b/src/release/bin/startup.sh @@ -62,8 +62,8 @@ if [ -z $GEOSERVER_DATA_DIR ]; then fi # if not told otherwise pump up the permgen -if [ -z "$JAVA_OPS" ]; then - set JAVA_OPS=-XX:MaxPermSize=128m +if [ -z "$JAVA_OPTS" ]; then + set JAVA_OPTS=-XX:MaxPermSize=128m fi cd "$GEOSERVER_HOME" From 6572a6b57026dab5766e0b748f45571c1b39c32c Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sat, 1 Sep 2012 17:29:26 +0200 Subject: [PATCH 71/72] More fixes to startup scripts --- src/release/bin/startup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/release/bin/startup.sh b/src/release/bin/startup.sh index 511cee48d38..459859229e4 100755 --- a/src/release/bin/startup.sh +++ b/src/release/bin/startup.sh @@ -63,7 +63,7 @@ fi # if not told otherwise pump up the permgen if [ -z "$JAVA_OPTS" ]; then - set JAVA_OPTS=-XX:MaxPermSize=128m + export JAVA_OPTS=-XX:MaxPermSize=128m fi cd "$GEOSERVER_HOME" From dd4c2caae4e7266646474374b2daf5d72bcf9bef Mon Sep 17 00:00:00 2001 From: aaime Date: Sat, 1 Sep 2012 15:37:49 +0000 Subject: [PATCH 72/72] updating version numbers and release notes for 2.2-RC3 --- doc/en/developer/source/conf.py | 2 +- doc/en/docguide/source/conf.py | 2 +- doc/en/user/source/conf.py | 2 +- doc/es/user/source/conf.py | 2 +- doc/fr/user/source/conf.py | 2 +- src/community/app-schema/pom.xml | 2 +- .../app-schema/webservice-test/pom.xml | 2 +- src/community/authkey/pom.xml | 2 +- src/community/css/pom.xml | 4 ++-- src/community/dbconfig/pom.xml | 4 ++-- src/community/dds/pom.xml | 4 ++-- src/community/dxf/pom.xml | 4 ++-- src/community/feature-aggregate/pom.xml | 2 +- src/community/ftp/pom.xml | 4 ++-- src/community/geoxacml/pom.xml | 2 +- src/community/gss/pom.xml | 4 ++-- src/community/hello/pom.xml | 4 ++-- src/community/hello_web/pom.xml | 4 ++-- src/community/inspire/pom.xml | 4 ++-- src/community/istyler/pom.xml | 10 +++++----- src/community/monitoring/pom.xml | 18 +++++++++--------- src/community/pom.xml | 2 +- src/community/printing/pom.xml | 4 ++-- src/community/proxy/pom.xml | 4 ++-- src/community/python/geoscript | 1 + src/community/python/pom.xml | 18 +++++++++--------- src/community/release/pom.xml | 2 +- src/community/scriptlet/pom.xml | 12 ++++++------ src/community/sfs/pom.xml | 2 +- src/community/sldService/pom.xml | 10 +++++----- src/community/spatialite/pom.xml | 8 ++++---- src/community/wfsv/pom.xml | 2 +- src/community/wps-sextante/pom.xml | 2 +- .../app-schema/app-schema-oracle-test/pom.xml | 2 +- .../app-schema/app-schema-postgis-test/pom.xml | 2 +- .../app-schema/app-schema-test/pom.xml | 2 +- src/extension/app-schema/pom.xml | 2 +- .../app-schema/sample-data-access-test/pom.xml | 2 +- src/extension/arcsde/pom.xml | 2 +- src/extension/charts/pom.xml | 2 +- src/extension/control-flow/pom.xml | 4 ++-- src/extension/db2/pom.xml | 2 +- src/extension/excel/pom.xml | 2 +- src/extension/feature-pregeneralized/pom.xml | 4 ++-- src/extension/gdal/pom.xml | 2 +- src/extension/geosearch/pom.xml | 12 ++++++------ src/extension/h2/pom.xml | 2 +- src/extension/imagemap/pom.xml | 2 +- src/extension/imagemosaic-jdbc/pom.xml | 2 +- src/extension/jp2k/pom.xml | 2 +- src/extension/mysql/pom.xml | 2 +- src/extension/ogr/pom.xml | 2 +- src/extension/oracle/pom.xml | 2 +- src/extension/pom.xml | 2 +- src/extension/querylayer/pom.xml | 2 +- src/extension/sqlserver/pom.xml | 2 +- src/extension/teradata/pom.xml | 2 +- src/extension/validation/pom.xml | 4 ++-- src/extension/wps/pom.xml | 2 +- src/extension/wps/web-wps/pom.xml | 2 +- src/extension/wps/wps-core/pom.xml | 2 +- src/gwc/pom.xml | 2 +- src/main/pom.xml | 2 +- src/maven/archetype/pom.xml | 2 +- src/maven/archetype/webPlugin/pom.xml | 2 +- .../main/resources/archetype-resources/pom.xml | 6 +++--- src/maven/archetype/wfsOutputFormat/pom.xml | 2 +- .../main/resources/archetype-resources/pom.xml | 6 +++--- src/maven/config/pom.xml | 2 +- src/maven/pom.xml | 2 +- src/ows/pom.xml | 2 +- src/platform/pom.xml | 2 +- src/pom.xml | 8 ++++---- src/release/RELEASE_NOTES.txt | 10 +++++----- src/release/bin.xml | 18 +++++++++--------- .../mac/GeoServer.app/Contents/Info.plist | 4 ++-- src/release/installer/mac/console/pom.xml | 2 +- src/release/installer/win/GeoServerEXE.nsi | 2 +- src/release/installer/win/wrapper.conf | 4 ++-- src/release/pom.xml | 2 +- src/release/src.xml | 6 +++--- src/rest/pom.xml | 2 +- src/restconfig/pom.xml | 8 ++++---- src/security/cas/pom.xml | 4 ++-- src/security/jdbc/pom.xml | 4 ++-- src/security/ldap/pom.xml | 4 ++-- src/security/pom.xml | 4 ++-- src/wcs/pom.xml | 2 +- src/wcs1_0/pom.xml | 2 +- src/wcs1_1/pom.xml | 2 +- src/web/app/pom.xml | 12 ++++++------ src/web/core/pom.xml | 4 ++-- src/web/demo/pom.xml | 4 ++-- src/web/gwc/pom.xml | 4 ++-- src/web/pom.xml | 4 ++-- src/web/security/pom.xml | 4 ++-- src/web/wcs/pom.xml | 4 ++-- src/web/wfs/pom.xml | 4 ++-- src/web/wms/pom.xml | 4 ++-- src/wfs/pom.xml | 2 +- src/wms/pom.xml | 2 +- 101 files changed, 195 insertions(+), 194 deletions(-) create mode 160000 src/community/python/geoscript diff --git a/doc/en/developer/source/conf.py b/doc/en/developer/source/conf.py index 167a95328d4..b5fcd01474e 100644 --- a/doc/en/developer/source/conf.py +++ b/doc/en/developer/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '2.2' # The full version, including alpha/beta/rc tags. -release = '2.2-SNAPSHOT' +release = '2.2-RC3' # Users don't need to see the "SNAPSHOT" notation when it's there if release.find('SNAPSHOT') != -1: release = '2.2.x' diff --git a/doc/en/docguide/source/conf.py b/doc/en/docguide/source/conf.py index dd6096d01f7..9b0080a3803 100644 --- a/doc/en/docguide/source/conf.py +++ b/doc/en/docguide/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '2.2' # The full version, including alpha/beta/rc tags. -release = '2.2-SNAPSHOT' +release = '2.2-RC3' # Users don't need to see the "SNAPSHOT" notation when it's there if release.find('SNAPSHOT') != -1: release = '2.2.x' diff --git a/doc/en/user/source/conf.py b/doc/en/user/source/conf.py index 263da6addf9..56120c21d25 100644 --- a/doc/en/user/source/conf.py +++ b/doc/en/user/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '2.2' # The full version, including alpha/beta/rc tags. -release = '2.2-SNAPSHOT' +release = '2.2-RC3' # Users don't need to see the "SNAPSHOT" notation when it's there if release.find('SNAPSHOT') != -1: release = '2.2.x' diff --git a/doc/es/user/source/conf.py b/doc/es/user/source/conf.py index 9e1c425dc87..cfd6e91f937 100644 --- a/doc/es/user/source/conf.py +++ b/doc/es/user/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '2.2' # The full version, including alpha/beta/rc tags. -release = '2.2-SNAPSHOT' +release = '2.2-RC3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/fr/user/source/conf.py b/doc/fr/user/source/conf.py index 3a409461d94..c6e9b09cd24 100644 --- a/doc/fr/user/source/conf.py +++ b/doc/fr/user/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '2.2' # The full version, including alpha/beta/rc tags. -release = '2.2-SNAPSHOT' +release = '2.2-RC3' # Users don't need to see the "SNAPSHOT" notation when it's there if release.find('SNAPSHOT') != -1: release = '2.2.x' diff --git a/src/community/app-schema/pom.xml b/src/community/app-schema/pom.xml index 3f69fd2e4ca..88a8d1282ea 100644 --- a/src/community/app-schema/pom.xml +++ b/src/community/app-schema/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/community/app-schema/webservice-test/pom.xml b/src/community/app-schema/webservice-test/pom.xml index c54f98617d2..45a90d59ac1 100644 --- a/src/community/app-schema/webservice-test/pom.xml +++ b/src/community/app-schema/webservice-test/pom.xml @@ -7,7 +7,7 @@ org.geoserver app-schema-community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/community/authkey/pom.xml b/src/community/authkey/pom.xml index 64be0a0689e..48d17660b41 100644 --- a/src/community/authkey/pom.xml +++ b/src/community/authkey/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community diff --git a/src/community/css/pom.xml b/src/community/css/pom.xml index 54e93485324..14acca3b747 100644 --- a/src/community/css/pom.xml +++ b/src/community/css/pom.xml @@ -3,12 +3,12 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community css jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer CSS Styling diff --git a/src/community/dbconfig/pom.xml b/src/community/dbconfig/pom.xml index 403a1f068a6..e67a635fe0f 100644 --- a/src/community/dbconfig/pom.xml +++ b/src/community/dbconfig/pom.xml @@ -5,13 +5,13 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community dbconfig jar - 2.2-SNAPSHOT + 2.2-RC3 Hibernate Catalog/Configuration Module diff --git a/src/community/dds/pom.xml b/src/community/dds/pom.xml index d30084d808f..f3ac13ce293 100755 --- a/src/community/dds/pom.xml +++ b/src/community/dds/pom.xml @@ -9,13 +9,13 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community dds jar - 2.2-SNAPSHOT + 2.2-RC3 WorldWind Format Module diff --git a/src/community/dxf/pom.xml b/src/community/dxf/pom.xml index fbbf9099082..ed52c24c7a1 100644 --- a/src/community/dxf/pom.xml +++ b/src/community/dxf/pom.xml @@ -8,13 +8,13 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver dxf jar - 2.2-SNAPSHOT + 2.2-RC3 DXF output format diff --git a/src/community/feature-aggregate/pom.xml b/src/community/feature-aggregate/pom.xml index 69fe4dcb520..a262151e257 100644 --- a/src/community/feature-aggregate/pom.xml +++ b/src/community/feature-aggregate/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community diff --git a/src/community/ftp/pom.xml b/src/community/ftp/pom.xml index 42c3fd33b14..5eabf440895 100644 --- a/src/community/ftp/pom.xml +++ b/src/community/ftp/pom.xml @@ -3,12 +3,12 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community ftp jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer Embedded FTP server diff --git a/src/community/geoxacml/pom.xml b/src/community/geoxacml/pom.xml index 0f796dce3fb..71bbe9ade99 100644 --- a/src/community/geoxacml/pom.xml +++ b/src/community/geoxacml/pom.xml @@ -7,7 +7,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/community/gss/pom.xml b/src/community/gss/pom.xml index 35241857838..7631547f518 100644 --- a/src/community/gss/pom.xml +++ b/src/community/gss/pom.xml @@ -3,12 +3,12 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community gss jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer Synchronization Service diff --git a/src/community/hello/pom.xml b/src/community/hello/pom.xml index 3ddfcfefc6c..7807d3f4903 100644 --- a/src/community/hello/pom.xml +++ b/src/community/hello/pom.xml @@ -7,7 +7,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver @@ -20,7 +20,7 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 diff --git a/src/community/hello_web/pom.xml b/src/community/hello_web/pom.xml index 198b5ec0229..389392b42e3 100644 --- a/src/community/hello_web/pom.xml +++ b/src/community/hello_web/pom.xml @@ -6,7 +6,7 @@ org.geoserver web2 - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver @@ -19,7 +19,7 @@ org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 diff --git a/src/community/inspire/pom.xml b/src/community/inspire/pom.xml index 91e615e3cde..4b70bd0563a 100644 --- a/src/community/inspire/pom.xml +++ b/src/community/inspire/pom.xml @@ -5,13 +5,13 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community inspire jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer INSPIRE Extensions diff --git a/src/community/istyler/pom.xml b/src/community/istyler/pom.xml index 2ef773f5aea..a99e6fd4fc2 100644 --- a/src/community/istyler/pom.xml +++ b/src/community/istyler/pom.xml @@ -5,22 +5,22 @@ community org.geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community istyler - 2.2-SNAPSHOT + 2.2-RC3 Interactive Styler Web Plugin org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 tests test @@ -33,7 +33,7 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 tests test diff --git a/src/community/monitoring/pom.xml b/src/community/monitoring/pom.xml index e328a70b0ba..ae58e30e137 100644 --- a/src/community/monitoring/pom.xml +++ b/src/community/monitoring/pom.xml @@ -4,7 +4,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community monitoring @@ -14,17 +14,17 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver rest - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 javax.persistence @@ -79,32 +79,32 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 test tests org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 test org.geoserver wms - 2.2-SNAPSHOT + 2.2-RC3 test org.geoserver wcs1_0 - 2.2-SNAPSHOT + 2.2-RC3 test org.geoserver wcs1_1 - 2.2-SNAPSHOT + 2.2-RC3 test diff --git a/src/community/pom.xml b/src/community/pom.xml index 0535aad6167..78df93a4e0e 100644 --- a/src/community/pom.xml +++ b/src/community/pom.xml @@ -5,7 +5,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/community/printing/pom.xml b/src/community/printing/pom.xml index c82699b0e38..43daa66c23f 100644 --- a/src/community/printing/pom.xml +++ b/src/community/printing/pom.xml @@ -7,13 +7,13 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community printing jar - 2.2-SNAPSHOT + 2.2-RC3 Printing Module diff --git a/src/community/proxy/pom.xml b/src/community/proxy/pom.xml index 1f055268725..32c446f3337 100644 --- a/src/community/proxy/pom.xml +++ b/src/community/proxy/pom.xml @@ -5,12 +5,12 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community proxy jar - 2.2-SNAPSHOT + 2.2-RC3 HTTP Proxy Extension diff --git a/src/community/python/geoscript b/src/community/python/geoscript new file mode 160000 index 00000000000..ff668848cd8 --- /dev/null +++ b/src/community/python/geoscript @@ -0,0 +1 @@ +Subproject commit ff668848cd889a424401e578acef87bb192aeb8a diff --git a/src/community/python/pom.xml b/src/community/python/pom.xml index eacdb2352c8..0691cc23bb7 100644 --- a/src/community/python/pom.xml +++ b/src/community/python/pom.xml @@ -4,7 +4,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community python @@ -25,7 +25,7 @@ org.geoserver rest - 2.2-SNAPSHOT + 2.2-RC3 org.restlet @@ -34,27 +34,27 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver wms - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 org.geotools @@ -79,14 +79,14 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 test tests org.geoserver wms - 2.2-SNAPSHOT + 2.2-RC3 test tests diff --git a/src/community/release/pom.xml b/src/community/release/pom.xml index 8d54774c346..de2a20f90e2 100644 --- a/src/community/release/pom.xml +++ b/src/community/release/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community release diff --git a/src/community/scriptlet/pom.xml b/src/community/scriptlet/pom.xml index ccd62e8f0de..cfe061b6e6b 100644 --- a/src/community/scriptlet/pom.xml +++ b/src/community/scriptlet/pom.xml @@ -5,12 +5,12 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community scriptlet jar - 2.2-SNAPSHOT + 2.2-RC3 JavaScript REST Extension Support @@ -26,22 +26,22 @@ org.geoserver rest - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 tests test diff --git a/src/community/sfs/pom.xml b/src/community/sfs/pom.xml index 4cde98d33f0..8697c8e6514 100644 --- a/src/community/sfs/pom.xml +++ b/src/community/sfs/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community diff --git a/src/community/sldService/pom.xml b/src/community/sldService/pom.xml index 36eb7bbe4e7..84b06229b83 100644 --- a/src/community/sldService/pom.xml +++ b/src/community/sldService/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community @@ -18,22 +18,22 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver rest - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver restconfig - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver wms - 2.2-SNAPSHOT + 2.2-RC3 jdom diff --git a/src/community/spatialite/pom.xml b/src/community/spatialite/pom.xml index 72f23bafe02..2dc435371fa 100644 --- a/src/community/spatialite/pom.xml +++ b/src/community/spatialite/pom.xml @@ -6,7 +6,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community @@ -38,7 +38,7 @@ org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 junit @@ -49,14 +49,14 @@ org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 tests test org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 tests test diff --git a/src/community/wfsv/pom.xml b/src/community/wfsv/pom.xml index 071555beb90..7511e2fcbe0 100644 --- a/src/community/wfsv/pom.xml +++ b/src/community/wfsv/pom.xml @@ -7,7 +7,7 @@ org.geoserver community - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/community/wps-sextante/pom.xml b/src/community/wps-sextante/pom.xml index 02381d942c9..bd246b6ffa0 100644 --- a/src/community/wps-sextante/pom.xml +++ b/src/community/wps-sextante/pom.xml @@ -7,7 +7,7 @@ org.geoserver.community wps - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.community diff --git a/src/extension/app-schema/app-schema-oracle-test/pom.xml b/src/extension/app-schema/app-schema-oracle-test/pom.xml index f2579735117..4aa385d74ed 100644 --- a/src/extension/app-schema/app-schema-oracle-test/pom.xml +++ b/src/extension/app-schema/app-schema-oracle-test/pom.xml @@ -7,7 +7,7 @@ org.geoserver.extension app-schema - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/app-schema/app-schema-postgis-test/pom.xml b/src/extension/app-schema/app-schema-postgis-test/pom.xml index 15d8827d009..0f7316e775b 100644 --- a/src/extension/app-schema/app-schema-postgis-test/pom.xml +++ b/src/extension/app-schema/app-schema-postgis-test/pom.xml @@ -7,7 +7,7 @@ org.geoserver.extension app-schema - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/app-schema/app-schema-test/pom.xml b/src/extension/app-schema/app-schema-test/pom.xml index bed36b3ab79..1a85ecb8c68 100644 --- a/src/extension/app-schema/app-schema-test/pom.xml +++ b/src/extension/app-schema/app-schema-test/pom.xml @@ -7,7 +7,7 @@ org.geoserver.extension app-schema - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/app-schema/pom.xml b/src/extension/app-schema/pom.xml index de6722ebb71..48382aec36a 100644 --- a/src/extension/app-schema/pom.xml +++ b/src/extension/app-schema/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/app-schema/sample-data-access-test/pom.xml b/src/extension/app-schema/sample-data-access-test/pom.xml index 584342e08bb..ca05417c323 100644 --- a/src/extension/app-schema/sample-data-access-test/pom.xml +++ b/src/extension/app-schema/sample-data-access-test/pom.xml @@ -7,7 +7,7 @@ org.geoserver.extension app-schema - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/arcsde/pom.xml b/src/extension/arcsde/pom.xml index b468db10f44..3a4aaa5eec6 100644 --- a/src/extension/arcsde/pom.xml +++ b/src/extension/arcsde/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/charts/pom.xml b/src/extension/charts/pom.xml index a71961b2ad6..b70f350a2e4 100644 --- a/src/extension/charts/pom.xml +++ b/src/extension/charts/pom.xml @@ -3,7 +3,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 4.0.0 diff --git a/src/extension/control-flow/pom.xml b/src/extension/control-flow/pom.xml index d3a7cc511fe..e6bd49d4df4 100644 --- a/src/extension/control-flow/pom.xml +++ b/src/extension/control-flow/pom.xml @@ -5,11 +5,11 @@ extension org.geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension control-flow - 2.2-SNAPSHOT + 2.2-RC3 OWS request flow controller diff --git a/src/extension/db2/pom.xml b/src/extension/db2/pom.xml index 6e4b90c44ab..6ba3699f82c 100644 --- a/src/extension/db2/pom.xml +++ b/src/extension/db2/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/excel/pom.xml b/src/extension/excel/pom.xml index 612c7dc39e0..2d6ee8abda7 100644 --- a/src/extension/excel/pom.xml +++ b/src/extension/excel/pom.xml @@ -4,7 +4,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 4.0.0 diff --git a/src/extension/feature-pregeneralized/pom.xml b/src/extension/feature-pregeneralized/pom.xml index ec453586b7a..a7243ec9478 100644 --- a/src/extension/feature-pregeneralized/pom.xml +++ b/src/extension/feature-pregeneralized/pom.xml @@ -7,13 +7,13 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension feature-pregeneralized jar - 2.2-SNAPSHOT + 2.2-RC3 Feature Generalization Extension diff --git a/src/extension/gdal/pom.xml b/src/extension/gdal/pom.xml index f31aac5b53b..32dd10b8a3a 100644 --- a/src/extension/gdal/pom.xml +++ b/src/extension/gdal/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/geosearch/pom.xml b/src/extension/geosearch/pom.xml index 1956a99b1f5..e96c96de91c 100644 --- a/src/extension/geosearch/pom.xml +++ b/src/extension/geosearch/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension @@ -18,22 +18,22 @@ org.geoserver rest - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver wms - 2.2-SNAPSHOT + 2.2-RC3 stax @@ -47,7 +47,7 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 tests test diff --git a/src/extension/h2/pom.xml b/src/extension/h2/pom.xml index a54a264d575..bdec4be4118 100644 --- a/src/extension/h2/pom.xml +++ b/src/extension/h2/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/imagemap/pom.xml b/src/extension/imagemap/pom.xml index 0671e342cf4..271069482d6 100644 --- a/src/extension/imagemap/pom.xml +++ b/src/extension/imagemap/pom.xml @@ -7,7 +7,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/imagemosaic-jdbc/pom.xml b/src/extension/imagemosaic-jdbc/pom.xml index 54aac1c793c..ad234ee22ca 100644 --- a/src/extension/imagemosaic-jdbc/pom.xml +++ b/src/extension/imagemosaic-jdbc/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/jp2k/pom.xml b/src/extension/jp2k/pom.xml index aaff07365d3..11495c922e1 100644 --- a/src/extension/jp2k/pom.xml +++ b/src/extension/jp2k/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/mysql/pom.xml b/src/extension/mysql/pom.xml index d90e4cc66bf..8c800ba2dca 100644 --- a/src/extension/mysql/pom.xml +++ b/src/extension/mysql/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/ogr/pom.xml b/src/extension/ogr/pom.xml index 07581389ba0..a0c2c02a943 100644 --- a/src/extension/ogr/pom.xml +++ b/src/extension/ogr/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/oracle/pom.xml b/src/extension/oracle/pom.xml index 1e7a5f34b50..176019bf8d8 100644 --- a/src/extension/oracle/pom.xml +++ b/src/extension/oracle/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/pom.xml b/src/extension/pom.xml index a2547985554..4c12ae3ea6f 100644 --- a/src/extension/pom.xml +++ b/src/extension/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/extension/querylayer/pom.xml b/src/extension/querylayer/pom.xml index 785817d3394..945ca91ea2b 100644 --- a/src/extension/querylayer/pom.xml +++ b/src/extension/querylayer/pom.xml @@ -5,7 +5,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension querylayer diff --git a/src/extension/sqlserver/pom.xml b/src/extension/sqlserver/pom.xml index 079a9595825..8ec7bc56f6c 100644 --- a/src/extension/sqlserver/pom.xml +++ b/src/extension/sqlserver/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/teradata/pom.xml b/src/extension/teradata/pom.xml index 35404eca078..f821af811df 100644 --- a/src/extension/teradata/pom.xml +++ b/src/extension/teradata/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/validation/pom.xml b/src/extension/validation/pom.xml index 4b3cfe05012..47afe06c948 100644 --- a/src/extension/validation/pom.xml +++ b/src/extension/validation/pom.xml @@ -6,7 +6,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension @@ -18,7 +18,7 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 diff --git a/src/extension/wps/pom.xml b/src/extension/wps/pom.xml index d0f4c8f3682..2ca2d6fc695 100644 --- a/src/extension/wps/pom.xml +++ b/src/extension/wps/pom.xml @@ -7,7 +7,7 @@ org.geoserver extension - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/wps/web-wps/pom.xml b/src/extension/wps/web-wps/pom.xml index 80e927f05b9..7fc26b91136 100644 --- a/src/extension/wps/web-wps/pom.xml +++ b/src/extension/wps/web-wps/pom.xml @@ -7,7 +7,7 @@ org.geoserver.extension wps - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/extension/wps/wps-core/pom.xml b/src/extension/wps/wps-core/pom.xml index ec1834b02ab..f8e8ad61b37 100644 --- a/src/extension/wps/wps-core/pom.xml +++ b/src/extension/wps/wps-core/pom.xml @@ -7,7 +7,7 @@ org.geoserver.extension wps - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.extension diff --git a/src/gwc/pom.xml b/src/gwc/pom.xml index e7bfdb97e44..a5a87e7bc19 100644 --- a/src/gwc/pom.xml +++ b/src/gwc/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/main/pom.xml b/src/main/pom.xml index 70d30014d5d..e4109ca7fd3 100644 --- a/src/main/pom.xml +++ b/src/main/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/maven/archetype/pom.xml b/src/maven/archetype/pom.xml index 26b7744abe0..db9bf4471dd 100644 --- a/src/maven/archetype/pom.xml +++ b/src/maven/archetype/pom.xml @@ -6,7 +6,7 @@ txsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/m org.geoserver maven - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.maven diff --git a/src/maven/archetype/webPlugin/pom.xml b/src/maven/archetype/webPlugin/pom.xml index c7b684643cb..be9057b4b81 100644 --- a/src/maven/archetype/webPlugin/pom.xml +++ b/src/maven/archetype/webPlugin/pom.xml @@ -5,7 +5,7 @@ org.geoserver.maven archetype - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.maven diff --git a/src/maven/archetype/webPlugin/src/main/resources/archetype-resources/pom.xml b/src/maven/archetype/webPlugin/src/main/resources/archetype-resources/pom.xml index d2379191483..750e96b21f9 100644 --- a/src/maven/archetype/webPlugin/src/main/resources/archetype-resources/pom.xml +++ b/src/maven/archetype/webPlugin/src/main/resources/archetype-resources/pom.xml @@ -19,12 +19,12 @@ org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core - 2.2-SNAPSHOT + 2.2-RC3 tests test @@ -37,7 +37,7 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 tests test diff --git a/src/maven/archetype/wfsOutputFormat/pom.xml b/src/maven/archetype/wfsOutputFormat/pom.xml index 8bd64635738..ef358ec1d12 100644 --- a/src/maven/archetype/wfsOutputFormat/pom.xml +++ b/src/maven/archetype/wfsOutputFormat/pom.xml @@ -5,7 +5,7 @@ org.geoserver.maven archetype - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.maven diff --git a/src/maven/archetype/wfsOutputFormat/src/main/resources/archetype-resources/pom.xml b/src/maven/archetype/wfsOutputFormat/src/main/resources/archetype-resources/pom.xml index 5377cecd69f..0eb1d0e9a02 100644 --- a/src/maven/archetype/wfsOutputFormat/src/main/resources/archetype-resources/pom.xml +++ b/src/maven/archetype/wfsOutputFormat/src/main/resources/archetype-resources/pom.xml @@ -29,7 +29,7 @@ org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 junit @@ -40,14 +40,14 @@ org.geoserver wfs - 2.2-SNAPSHOT + 2.2-RC3 tests test org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 tests test diff --git a/src/maven/config/pom.xml b/src/maven/config/pom.xml index 39d394fbe46..1dd7b025dbe 100644 --- a/src/maven/config/pom.xml +++ b/src/maven/config/pom.xml @@ -6,7 +6,7 @@ org.geoserver maven - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.maven diff --git a/src/maven/pom.xml b/src/maven/pom.xml index a9099d4cef9..c95703487fe 100644 --- a/src/maven/pom.xml +++ b/src/maven/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/ows/pom.xml b/src/ows/pom.xml index e8e4e0e52ef..95c85ad0220 100644 --- a/src/ows/pom.xml +++ b/src/ows/pom.xml @@ -5,7 +5,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/platform/pom.xml b/src/platform/pom.xml index 15fcda4d0dc..46d7fbc3944 100644 --- a/src/platform/pom.xml +++ b/src/platform/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/pom.xml b/src/pom.xml index 2efd0c3051e..5611d270209 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -8,7 +8,7 @@ org.geoserver geoserver pom - 2.2-SNAPSHOT + 2.2-RC3 GeoServer @@ -1169,7 +1169,7 @@ release/ext-querylayer.xml release/ext-teradata.xml - geoserver-2.2-SNAPSHOT + geoserver-2.2-RC3 ${project.build.directory}/release @@ -1408,8 +1408,8 @@ - 2.2-SNAPSHOT - 8-SNAPSHOT + 2.2-RC3 + 8.1 1.3-RC5 3.1.1.RELEASE 3.1.0.RELEASE diff --git a/src/release/RELEASE_NOTES.txt b/src/release/RELEASE_NOTES.txt index c1e15dfc90b..29067ebeb89 100644 --- a/src/release/RELEASE_NOTES.txt +++ b/src/release/RELEASE_NOTES.txt @@ -1,11 +1,11 @@ -GeoServer @VER@ +GeoServer 2.2-RC3 ------------------- -This release was built on @DATE@. The changelog for this release can be found: +This release was built on Sep 01, 2012. The changelog for this release can be found: - http://jira.codehaus.org/browse/GEOS/fixforversion/@JIRA_VER@ + http://jira.codehaus.org/browse/GEOS/fixforversion/18700 This release is based on: - GeoTools @GT_VER@ - GeoWebCache @GWC_VER@ + GeoTools 8.1 + GeoWebCache 1.3-RC5 diff --git a/src/release/bin.xml b/src/release/bin.xml index 96b01b3b613..e3e84cd5989 100644 --- a/src/release/bin.xml +++ b/src/release/bin.xml @@ -8,7 +8,7 @@ web/app/target/geoserver - geoserver-2.2-SNAPSHOT/webapps/geoserver + geoserver-2.2-RC3/webapps/geoserver **/* @@ -22,7 +22,7 @@ web/app/target/geoserver/data - geoserver-2.2-SNAPSHOT/data_dir + geoserver-2.2-RC3/data_dir **/* @@ -30,7 +30,7 @@ release/target/dependency - geoserver-2.2-SNAPSHOT/lib + geoserver-2.2-RC3/lib ant-*.jar commons-el-*.jar @@ -52,7 +52,7 @@ release/bin keep - geoserver-2.2-SNAPSHOT/bin + geoserver-2.2-RC3/bin *.bat @@ -61,7 +61,7 @@ release/bin unix - geoserver-2.2-SNAPSHOT/bin + geoserver-2.2-RC3/bin 0755 0755 @@ -71,7 +71,7 @@ release - geoserver-2.2-SNAPSHOT + geoserver-2.2-RC3 LICENSE.txt GPL.txt @@ -82,7 +82,7 @@ target - geoserver-2.2-SNAPSHOT + geoserver-2.2-RC3 VERSION.txt @@ -90,7 +90,7 @@ release/jetty - geoserver-2.2-SNAPSHOT + geoserver-2.2-RC3 **/* @@ -98,7 +98,7 @@ release/logs - geoserver-2.2-SNAPSHOT + geoserver-2.2-RC3 diff --git a/src/release/installer/mac/GeoServer.app/Contents/Info.plist b/src/release/installer/mac/GeoServer.app/Contents/Info.plist index 794c1be2996..b23fc60bfa3 100644 --- a/src/release/installer/mac/GeoServer.app/Contents/Info.plist +++ b/src/release/installer/mac/GeoServer.app/Contents/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier org.geoserver CFBundleVersion - 2.2-SNAPSHOT + 2.2-RC3 CFBundleAllowMixedLocalizations true CFBundleDevelopmentRegion @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.2-SNAPSHOT + 2.2-RC3 CFBundleSignature ???? CFBundleGetInfoString diff --git a/src/release/installer/mac/console/pom.xml b/src/release/installer/mac/console/pom.xml index 070282b95f6..13d64c0bcb1 100644 --- a/src/release/installer/mac/console/pom.xml +++ b/src/release/installer/mac/console/pom.xml @@ -6,7 +6,7 @@ 4.0.0 org.geoserver console - 2.2-SNAPSHOT + 2.2-RC3 GeoServer Console Monitor GeoServer Console diff --git a/src/release/installer/win/GeoServerEXE.nsi b/src/release/installer/win/GeoServerEXE.nsi index 18d7bd0396a..ce42a78c42f 100644 --- a/src/release/installer/win/GeoServerEXE.nsi +++ b/src/release/installer/win/GeoServerEXE.nsi @@ -2,7 +2,7 @@ ; Define your application name !define APPNAME "GeoServer" -!define VERSION "2.2-SNAPSHOT" +!define VERSION "2.2-RC3" ;!define LONGVERSION "2.0.0.0" !define APPNAMEANDVERSION "${APPNAME} ${VERSION}" diff --git a/src/release/installer/win/wrapper.conf b/src/release/installer/win/wrapper.conf index 13d38475cba..1da57d9d194 100644 --- a/src/release/installer/win/wrapper.conf +++ b/src/release/installer/win/wrapper.conf @@ -74,10 +74,10 @@ wrapper.syslog.loglevel=NONE # service can then be reinstalled. # Name of the service -wrapper.ntservice.name=GeoServer 2.2-SNAPSHOT +wrapper.ntservice.name=GeoServer 2.2-RC3 # Display name of the service -wrapper.ntservice.displayname=GeoServer 2.2-SNAPSHOT +wrapper.ntservice.displayname=GeoServer 2.2-RC3 # Description of the service wrapper.ntservice.description=GeoServer is an open source software server written in Java that allows users to share and edit geospatial data. diff --git a/src/release/pom.xml b/src/release/pom.xml index b46edaf949e..58472383a5e 100644 --- a/src/release/pom.xml +++ b/src/release/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver release diff --git a/src/release/src.xml b/src/release/src.xml index 6c1736334ff..7bc2f185aa5 100644 --- a/src/release/src.xml +++ b/src/release/src.xml @@ -25,7 +25,7 @@ org.geoserver:extension - geoserver-2.2-SNAPSHOT/${artifactId}/ + geoserver-2.2-RC3/${artifactId}/ target **/target @@ -36,7 +36,7 @@ . - geoserver-2.2-SNAPSHOT/ + geoserver-2.2-RC3/ pom.xml @@ -52,7 +52,7 @@ target - geoserver-2.2-SNAPSHOT + geoserver-2.2-RC3 VERSION.txt diff --git a/src/rest/pom.xml b/src/rest/pom.xml index f34d19ebfd5..5e18ba3e1fc 100644 --- a/src/rest/pom.xml +++ b/src/rest/pom.xml @@ -8,7 +8,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/restconfig/pom.xml b/src/restconfig/pom.xml index d3eba56b779..a6f4b868ca5 100644 --- a/src/restconfig/pom.xml +++ b/src/restconfig/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver @@ -18,17 +18,17 @@ org.geoserver main - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver rest - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver wms - 2.2-SNAPSHOT + 2.2-RC3 jdom diff --git a/src/security/cas/pom.xml b/src/security/cas/pom.xml index 12f2615ec5e..f0f4df5d14f 100644 --- a/src/security/cas/pom.xml +++ b/src/security/cas/pom.xml @@ -7,13 +7,13 @@ org.geoserver security - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.security sec-cas jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer CAS Security Module diff --git a/src/security/jdbc/pom.xml b/src/security/jdbc/pom.xml index 45dd2092954..1b5cac16c7e 100644 --- a/src/security/jdbc/pom.xml +++ b/src/security/jdbc/pom.xml @@ -7,12 +7,12 @@ org.geoserver security - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.security sec-jdbc jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer JDBC Security Module diff --git a/src/security/ldap/pom.xml b/src/security/ldap/pom.xml index eaf6d95d87c..15e6c1779ca 100644 --- a/src/security/ldap/pom.xml +++ b/src/security/ldap/pom.xml @@ -7,13 +7,13 @@ org.geoserver security - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.security sec-ldap jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer LDAP Security Module diff --git a/src/security/pom.xml b/src/security/pom.xml index 019c91e5d4b..4db2ad16d8f 100644 --- a/src/security/pom.xml +++ b/src/security/pom.xml @@ -7,13 +7,13 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver security pom - 2.2-SNAPSHOT + 2.2-RC3 GeoServer Security Modules diff --git a/src/wcs/pom.xml b/src/wcs/pom.xml index 5ef045a0644..4252b6df05c 100644 --- a/src/wcs/pom.xml +++ b/src/wcs/pom.xml @@ -6,7 +6,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/wcs1_0/pom.xml b/src/wcs1_0/pom.xml index 5e34f7574ca..af87aac2764 100644 --- a/src/wcs1_0/pom.xml +++ b/src/wcs1_0/pom.xml @@ -7,7 +7,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/wcs1_1/pom.xml b/src/wcs1_1/pom.xml index 47a51e9dca8..cf6dc902b5d 100644 --- a/src/wcs1_1/pom.xml +++ b/src/wcs1_1/pom.xml @@ -7,7 +7,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/web/app/pom.xml b/src/web/app/pom.xml index 882bba83d5d..7071ca6dd45 100644 --- a/src/web/app/pom.xml +++ b/src/web/app/pom.xml @@ -5,12 +5,12 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-app jar - 2.2-SNAPSHOT + 2.2-RC3 GeoServer Web Application @@ -308,7 +308,7 @@ org.geoserver.extension arcsde - 2.2-SNAPSHOT + 2.2-RC3 @@ -337,7 +337,7 @@ org.geoserver.extension sqlserver - 2.2-SNAPSHOT + 2.2-RC3 @@ -347,7 +347,7 @@ org.geoserver.extension oracle - 2.2-SNAPSHOT + 2.2-RC3 @@ -357,7 +357,7 @@ org.geoserver.extension mysql - 2.2-SNAPSHOT + 2.2-RC3 diff --git a/src/web/core/pom.xml b/src/web/core/pom.xml index 99b4a658984..0ed40b8d941 100644 --- a/src/web/core/pom.xml +++ b/src/web/core/pom.xml @@ -7,13 +7,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-core jar - 2.2-SNAPSHOT + 2.2-RC3 Core UI Module diff --git a/src/web/demo/pom.xml b/src/web/demo/pom.xml index b7c4814648c..f8ab458c9e1 100644 --- a/src/web/demo/pom.xml +++ b/src/web/demo/pom.xml @@ -10,13 +10,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-demo jar - 2.2-SNAPSHOT + 2.2-RC3 Demoes Module diff --git a/src/web/gwc/pom.xml b/src/web/gwc/pom.xml index da238926e3c..955a206677e 100644 --- a/src/web/gwc/pom.xml +++ b/src/web/gwc/pom.xml @@ -7,13 +7,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-gwc jar - 2.2-SNAPSHOT + 2.2-RC3 GWC UI Module diff --git a/src/web/pom.xml b/src/web/pom.xml index 06713f0c624..7c712c3ae2e 100644 --- a/src/web/pom.xml +++ b/src/web/pom.xml @@ -7,13 +7,13 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver web pom - 2.2-SNAPSHOT + 2.2-RC3 GeoServer Web Modules diff --git a/src/web/security/pom.xml b/src/web/security/pom.xml index 496580a2a67..0bdc97e846b 100644 --- a/src/web/security/pom.xml +++ b/src/web/security/pom.xml @@ -7,13 +7,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-security jar - 2.2-SNAPSHOT + 2.2-RC3 Security UI Module diff --git a/src/web/wcs/pom.xml b/src/web/wcs/pom.xml index 69f1fab5e9f..5dfc8b05a45 100644 --- a/src/web/wcs/pom.xml +++ b/src/web/wcs/pom.xml @@ -7,13 +7,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-wcs jar - 2.2-SNAPSHOT + 2.2-RC3 WCS UI Module diff --git a/src/web/wfs/pom.xml b/src/web/wfs/pom.xml index 22b0ac956c0..d5e969c8a22 100644 --- a/src/web/wfs/pom.xml +++ b/src/web/wfs/pom.xml @@ -7,13 +7,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-wfs jar - 2.2-SNAPSHOT + 2.2-RC3 WFS UI Module diff --git a/src/web/wms/pom.xml b/src/web/wms/pom.xml index 211bc99b90f..933bee2635b 100644 --- a/src/web/wms/pom.xml +++ b/src/web/wms/pom.xml @@ -7,13 +7,13 @@ org.geoserver web - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver.web web-wms jar - 2.2-SNAPSHOT + 2.2-RC3 WMS UI Module diff --git a/src/wfs/pom.xml b/src/wfs/pom.xml index 30b95725520..a1f16f77e5c 100644 --- a/src/wfs/pom.xml +++ b/src/wfs/pom.xml @@ -7,7 +7,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver diff --git a/src/wms/pom.xml b/src/wms/pom.xml index 0eaa48a4604..38140ce2182 100644 --- a/src/wms/pom.xml +++ b/src/wms/pom.xml @@ -7,7 +7,7 @@ org.geoserver geoserver - 2.2-SNAPSHOT + 2.2-RC3 org.geoserver