1 /******************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  MapCache tile caching: composite 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 compositeriction, 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 #include <apr_strings.h>
31 
32 typedef struct mapcache_cache_composite mapcache_cache_composite;
33 
34 typedef struct mapcache_cache_composite_cache_link mapcache_cache_composite_cache_link;
35 struct mapcache_cache_composite_cache_link {
36   mapcache_cache *cache;
37   int minzoom;
38   int maxzoom;
39   apr_array_header_t *grids;
40   apr_table_t *dimensions; /* key/value pairs of dimensions */
41 };
42 
43 struct mapcache_cache_composite {
44   mapcache_cache cache;
45   apr_array_header_t *cache_links;
46 };
47 
_mapcache_cache_link_create(apr_pool_t * pool)48 static mapcache_cache_composite_cache_link* _mapcache_cache_link_create(apr_pool_t *pool) {
49   mapcache_cache_composite_cache_link *cl = apr_pcalloc(pool, sizeof(mapcache_cache_composite_cache_link));
50   cl->cache=NULL;
51   cl->dimensions=NULL;
52   cl->grids=NULL;
53   cl->maxzoom=-1;
54   cl->minzoom=-1;
55   return cl;
56 }
57 /**
58  * returns the mapcache_cache to use for a given tile
59  * @param ctx
60  * @param tile
61  * @return
62  */
_mapcache_composite_cache_get(mapcache_context * ctx,mapcache_cache_composite * cache,mapcache_tile * tile)63 static mapcache_cache* _mapcache_composite_cache_get(mapcache_context *ctx, mapcache_cache_composite *cache, mapcache_tile *tile) {
64   int i;
65   for(i=0; i<cache->cache_links->nelts; i++) {
66     mapcache_cache_composite_cache_link *cache_link = APR_ARRAY_IDX(cache->cache_links,i,mapcache_cache_composite_cache_link*);
67     if(cache_link->minzoom != -1 && tile->z < cache_link->minzoom) continue;
68     if(cache_link->maxzoom != -1 && tile->z > cache_link->maxzoom) continue;
69     if(cache_link->grids) {
70       int j;
71       for(j=0;j<cache_link->grids->nelts;j++) {
72         char *grid_name = APR_ARRAY_IDX(cache_link->grids,j,char*);
73         if(!strcmp(tile->grid_link->grid->name,grid_name))
74           break;
75       }
76       /* not found */
77       if(j == cache_link->grids->nelts) continue;
78     }
79     if(cache_link->dimensions) {
80       const apr_array_header_t *array = apr_table_elts(cache_link->dimensions);
81       apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
82       int j;
83       if(!tile->dimensions) continue; /* the cache link refers to dimensions, but this tile does not have any, it cannot match */
84 
85       for (j = 0; j < array->nelts; j++) {
86         char *dim = elts[j].key;
87         char *dimval = elts[j].val;
88         int k;
89         for(k=0;k<tile->dimensions->nelts;k++) {
90           mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
91           if(!strcmp(rdim->dimension->name,dim) && !strcmp(rdim->cached_value,dimval))
92             break;
93         }
94         if(k == tile->dimensions->nelts) break; /* no tile dimension matched the current cache dimension */
95       }
96       if(j != array->nelts) continue; /* we broke out early from the cache dimension loop, so at least one was not correct */
97     }
98     return cache_link->cache;
99   }
100   ctx->set_error(ctx, 500, "no cache matches for given tile request");
101   return NULL;
102 }
103 
_mapcache_cache_composite_tile_exists(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)104 static int _mapcache_cache_composite_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
105 {
106   mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
107   mapcache_cache *subcache;
108   subcache = _mapcache_composite_cache_get(ctx, cache, tile);
109   if(GC_HAS_ERROR(ctx) || !subcache)
110     return MAPCACHE_FAILURE;
111   return mapcache_cache_tile_exists(ctx, subcache, tile);
112 }
113 
_mapcache_cache_composite_tile_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)114 static void _mapcache_cache_composite_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
115 {
116   mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
117   mapcache_cache *subcache;
118   subcache = _mapcache_composite_cache_get(ctx, cache, tile);
119   GC_CHECK_ERROR(ctx);
120   /*delete the tile itself*/
121   mapcache_cache_tile_delete(ctx,subcache,tile);
122 }
123 
124 /**
125  * \brief get content of given tile
126  *
127  * fills the mapcache_tile::data of the given tile with content stored on the composite server
128  * \private \memberof mapcache_cache_composite
129  * \sa mapcache_cache::tile_get()
130  */
_mapcache_cache_composite_tile_get(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)131 static int _mapcache_cache_composite_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
132 {
133   mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
134   mapcache_cache *subcache;
135   subcache = _mapcache_composite_cache_get(ctx, cache, tile);
136   GC_CHECK_ERROR_RETURN(ctx);
137   return mapcache_cache_tile_get(ctx,subcache,tile);
138 }
139 
_mapcache_cache_composite_tile_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)140 static void _mapcache_cache_composite_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
141 {
142   mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
143   mapcache_cache *subcache;
144   subcache = _mapcache_composite_cache_get(ctx, cache, tile);
145   GC_CHECK_ERROR(ctx);
146   return mapcache_cache_tile_set(ctx,subcache,tile);
147 }
148 
_mapcache_cache_composite_tile_multi_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tiles,int ntiles)149 static void _mapcache_cache_composite_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
150 {
151   mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
152   mapcache_cache *subcache;
153   subcache = _mapcache_composite_cache_get(ctx, cache, &tiles[0]);
154   GC_CHECK_ERROR(ctx);
155   return mapcache_cache_tile_multi_set(ctx,subcache,tiles,ntiles);
156 }
157 
158 /**
159  * \private \memberof mapcache_cache_composite
160  */
_mapcache_cache_composite_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * pcache,mapcache_cfg * config)161 static void _mapcache_cache_composite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
162 {
163   ezxml_t cur_node;
164   mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
165   cache->cache_links = apr_array_make(ctx->pool,3,sizeof(mapcache_cache_composite_cache_link*));
166   for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) {
167     char *sZoom;
168     int zoom;
169     mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt);
170     mapcache_cache_composite_cache_link *cachelink;
171     if(!refcache) {
172       ctx->set_error(ctx, 400, "composite cache \"%s\" references cache \"%s\","
173                      " but it is not configured (hint:referenced caches must be declared before this composite cache in the xml file)", pcache->name, cur_node->txt);
174       return;
175     }
176     cachelink = _mapcache_cache_link_create(ctx->pool);
177     cachelink->cache = refcache;
178 
179     sZoom = (char*)ezxml_attr(cur_node,"max-zoom");
180     if(sZoom) {
181       char *endptr;
182       zoom = (int)strtol(sZoom,&endptr,10);
183       if(*endptr != 0 || zoom < 0) {
184         ctx->set_error(ctx, 400, "failed to parse cache max-zoom %s (expecting a positive integer)",
185                        sZoom);
186         return;
187       }
188       cachelink->maxzoom = zoom;
189     }
190     sZoom = (char*)ezxml_attr(cur_node,"min-zoom");
191     if(sZoom) {
192       char *endptr;
193       zoom = (int)strtol(sZoom,&endptr,10);
194       if(*endptr != 0 || zoom < 0) {
195         ctx->set_error(ctx, 400, "failed to parse cache min-zoom %s (expecting a positive integer)",
196                        sZoom);
197         return;
198       }
199       cachelink->minzoom = zoom;
200     }
201     sZoom = (char*)ezxml_attr(cur_node,"grids");
202     if(sZoom) {
203       char *grids = apr_pstrdup(ctx->pool,sZoom),*key,*last;
204       for(key = apr_strtok(grids, ",", &last); key; key = apr_strtok(NULL,",",&last)) {
205         /*loop through grids*/
206         if(!cachelink->grids) {
207           cachelink->grids =apr_array_make(ctx->pool,1,sizeof(char*));
208         }
209         APR_ARRAY_PUSH(cachelink->grids,char*) = key;
210       }
211     }
212     sZoom = (char*)ezxml_attr(cur_node,"dimensions");
213     if(sZoom) {
214       char *dims = apr_pstrdup(ctx->pool,sZoom),*key,*last;
215       for(key = apr_strtok(dims, ",", &last); key; key = apr_strtok(NULL,",",&last)) {
216         char *dimname;
217         /*loop through dims*/
218         if(!cachelink->dimensions) {
219           cachelink->dimensions =apr_table_make(ctx->pool,1);
220         }
221         dimname = key;
222         while(*key && *key!='=') key++;
223         if(!(*key)) {
224           ctx->set_error(ctx,400,"failed to parse composite cache dimensions. expecting dimensions=\"dim1=val1,dim2=val2\"");
225           return;
226         }
227         *key = 0;
228         key++;
229         apr_table_set(cachelink->dimensions,dimname,key);
230       }
231     }
232 
233     APR_ARRAY_PUSH(cache->cache_links,mapcache_cache_composite_cache_link*) = cachelink;
234   }
235 }
236 
237 /**
238  * \private \memberof mapcache_cache_composite
239  */
_mapcache_cache_composite_configuration_post_config(mapcache_context * ctx,mapcache_cache * cache,mapcache_cfg * cfg)240 static void _mapcache_cache_composite_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
241     mapcache_cfg *cfg)
242 {
243 }
244 
245 
246 /**
247  * \brief creates and initializes a mapcache_cache_composite
248  */
mapcache_cache_composite_create(mapcache_context * ctx)249 mapcache_cache* mapcache_cache_composite_create(mapcache_context *ctx)
250 {
251   mapcache_cache_composite *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_composite));
252   if(!cache) {
253     ctx->set_error(ctx, 500, "failed to allocate composite cache");
254     return NULL;
255   }
256   cache->cache.metadata = apr_table_make(ctx->pool,3);
257   cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
258   cache->cache._tile_delete = _mapcache_cache_composite_tile_delete;
259   cache->cache._tile_get = _mapcache_cache_composite_tile_get;
260   cache->cache._tile_exists = _mapcache_cache_composite_tile_exists;
261   cache->cache._tile_set = _mapcache_cache_composite_tile_set;
262   cache->cache._tile_multi_set = _mapcache_cache_composite_tile_multi_set;
263   cache->cache.configuration_post_config = _mapcache_cache_composite_configuration_post_config;
264   cache->cache.configuration_parse_xml = _mapcache_cache_composite_configuration_parse_xml;
265   return (mapcache_cache*)cache;
266 }
267