1 /******************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  MapCache tile caching: fallback cache backend.
5  * Author:   Thomas Bonfort and the MapServer team.
6  *
7  ******************************************************************************
8  * Copyright (c) 1996-2011 Regents of the University of Minnesota.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without fallbackriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies of this Software or works derived from this Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *****************************************************************************/
28 
29 #include "mapcache.h"
30 
31 typedef struct mapcache_cache_fallback mapcache_cache_fallback;
32 
33 struct mapcache_cache_fallback {
34   mapcache_cache cache;
35   apr_array_header_t *caches;
36 };
37 
_mapcache_cache_fallback_tile_exists(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)38 static int _mapcache_cache_fallback_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
39 {
40   mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
41   mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
42   return mapcache_cache_tile_exists(ctx, subcache, tile);
43 }
44 
_mapcache_cache_fallback_tile_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)45 static void _mapcache_cache_fallback_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
46 {
47   mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
48   int i;
49   for(i=0; i<cache->caches->nelts; i++) {
50     mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
51     mapcache_cache_tile_delete(ctx, subcache, tile);
52     ctx->clear_errors(ctx); /* ignore errors */
53   }
54 }
55 
56 /**
57  * \brief get content of given tile
58  *
59  * fills the mapcache_tile::data of the given tile with content stored on the fallback server
60  * \private \memberof mapcache_cache_fallback
61  * \sa mapcache_cache::tile_get()
62  */
_mapcache_cache_fallback_tile_get(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)63 static int _mapcache_cache_fallback_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
64 {
65   mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
66   mapcache_cache *subcache;
67   int i,ret;
68   subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
69   ret = mapcache_cache_tile_get(ctx, subcache, tile);
70 
71   if(ret == MAPCACHE_FAILURE) {
72     int first_error = ctx->get_error(ctx);
73     char *first_error_message = ctx->get_error_message(ctx);
74     ctx->log(ctx,MAPCACHE_DEBUG,"failed \"GET\" on primary cache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\". Falling back on secondary caches",
75             APR_ARRAY_IDX(cache->caches,0,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
76     ctx->clear_errors(ctx);
77     for(i=1; i<cache->caches->nelts; i++) {
78       subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
79       if((ret = mapcache_cache_tile_get(ctx, subcache, tile)) == MAPCACHE_FAILURE) {
80         ctx->log(ctx,MAPCACHE_DEBUG,"failed \"GET\" on fallback cache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\". Continuing with other fallback caches if available",
81                 APR_ARRAY_IDX(cache->caches,0,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
82         ctx->clear_errors(ctx);
83         continue;
84       } else {
85         return ret;
86       }
87     }
88     /* all backends failed, return primary error message */
89     ctx->set_error(ctx,first_error,first_error_message);
90     return MAPCACHE_FAILURE;
91   } else {
92     /* success or notfound */
93     return ret;
94   }
95 }
96 
_mapcache_cache_fallback_tile_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)97 static void _mapcache_cache_fallback_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
98 {
99   mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
100   int i,first_error=0;
101   char *first_error_message;
102   for(i=0; i<cache->caches->nelts; i++) {
103     mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
104     mapcache_cache_tile_set(ctx, subcache, tile);
105     if(GC_HAS_ERROR(ctx)) {
106       if(!first_error) {
107         first_error = ctx->get_error(ctx);
108         first_error_message = ctx->get_error_message(ctx);
109       }
110       ctx->log(ctx,MAPCACHE_DEBUG,"failed \"SET\" on subcache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\"",
111               APR_ARRAY_IDX(cache->caches,i,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
112       ctx->clear_errors(ctx);
113     }
114   }
115   if(first_error) {
116     ctx->set_error(ctx,first_error,first_error_message);
117   }
118 }
119 
_mapcache_cache_fallback_tile_multi_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tiles,int ntiles)120 static void _mapcache_cache_fallback_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
121 {
122   mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
123   int i,first_error=0;
124   char *first_error_message;
125   for(i=0; i<cache->caches->nelts; i++) {
126     mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
127     mapcache_cache_tile_multi_set(ctx, subcache, tiles, ntiles);
128     if(GC_HAS_ERROR(ctx)) {
129       if(!first_error) {
130         first_error = ctx->get_error(ctx);
131         first_error_message = ctx->get_error_message(ctx);
132       }
133       ctx->log(ctx,MAPCACHE_DEBUG,"failed \"MULTISET\" on subcache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\"",
134               APR_ARRAY_IDX(cache->caches,i,mapcache_cache*)->name,tiles[0].z,tiles[0].x,tiles[0].y,tiles[0].tileset->name);
135       ctx->clear_errors(ctx);
136     }
137   }
138   if(first_error) {
139     ctx->set_error(ctx,first_error,first_error_message);
140   }
141 }
142 
143 /**
144  * \private \memberof mapcache_cache_fallback
145  */
_mapcache_cache_fallback_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * pcache,mapcache_cfg * config)146 static void _mapcache_cache_fallback_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
147 {
148   ezxml_t cur_node;
149   mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
150   cache->caches = apr_array_make(ctx->pool,3,sizeof(mapcache_cache*));
151   for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) {
152     mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt);
153     if(!refcache) {
154       ctx->set_error(ctx, 400, "fallback cache \"%s\" references cache \"%s\","
155                      " but it is not configured (hint:referenced caches must be declared before this fallback cache in the xml file)", pcache->name, cur_node->txt);
156       return;
157     }
158     APR_ARRAY_PUSH(cache->caches,mapcache_cache*) = refcache;
159   }
160   if(cache->caches->nelts == 0) {
161     ctx->set_error(ctx,400,"fallback cache \"%s\" does not reference any child caches", pcache->name);
162   }
163 }
164 
165 /**
166  * \private \memberof mapcache_cache_fallback
167  */
_mapcache_cache_fallback_configuration_post_config(mapcache_context * ctx,mapcache_cache * cache,mapcache_cfg * cfg)168 static void _mapcache_cache_fallback_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
169     mapcache_cfg *cfg)
170 {
171 }
172 
173 
174 /**
175  * \brief creates and initializes a mapcache_cache_fallback
176  */
mapcache_cache_fallback_create(mapcache_context * ctx)177 mapcache_cache* mapcache_cache_fallback_create(mapcache_context *ctx)
178 {
179   mapcache_cache_fallback *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_fallback));
180   if(!cache) {
181     ctx->set_error(ctx, 500, "failed to allocate fallback cache");
182     return NULL;
183   }
184   cache->cache.metadata = apr_table_make(ctx->pool,3);
185   cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
186   cache->cache._tile_delete = _mapcache_cache_fallback_tile_delete;
187   cache->cache._tile_get = _mapcache_cache_fallback_tile_get;
188   cache->cache._tile_exists = _mapcache_cache_fallback_tile_exists;
189   cache->cache._tile_set = _mapcache_cache_fallback_tile_set;
190   cache->cache._tile_multi_set = _mapcache_cache_fallback_tile_multi_set;
191   cache->cache.configuration_post_config = _mapcache_cache_fallback_configuration_post_config;
192   cache->cache.configuration_parse_xml = _mapcache_cache_fallback_configuration_parse_xml;
193   return (mapcache_cache*)cache;
194 }
195 
196