client_list.h 17.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef DATASRC_CONTAINER_H
#define DATASRC_CONTAINER_H

18
19
#include <util/memory_segment.h>

20
#include <dns/name.h>
21
#include <dns/rrclass.h>
22
23
#include <cc/data.h>
#include <exceptions/exceptions.h>
24

25
#include <vector>
26
#include <boost/shared_ptr.hpp>
27
#include <boost/scoped_ptr.hpp>
28
29
30
31
32
33
34
35
36
#include <boost/noncopyable.hpp>

namespace isc {
namespace datasrc {

class ZoneFinder;
typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
class DataSourceClient;
typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
37
38
39
class DataSourceClientContainer;
typedef boost::shared_ptr<DataSourceClientContainer>
    DataSourceClientContainerPtr;
40
41
42
43
44

// XXX: it's better to even hide the existence of the "memory" namespace.
// We should probably consider pimpl for details of ConfigurableClientList
// and hide real definitions except for itself and tests.
namespace memory {
45
class InMemoryClient;
46
class ZoneWriter;
47
}
48

49
/// \brief The list of data source clients.
50
///
51
/// The purpose of this class is to hold several data source clients and search
52
53
/// through them to find one containing a zone best matching a request.
///
54
/// All the data source clients should be for the same class. If you need
55
/// to handle multiple classes, you need to create multiple separate lists.
56
///
57
58
59
60
/// This is an abstract base class. It is not expected we would use multiple
/// implementation inside the servers (but it is not forbidden either), we
/// have it to allow easy testing. It is possible to create a mock-up class
/// instead of creating a full-blown configuration. The real implementation
61
62
/// is the ConfigurableClientList.
class ClientList : public boost::noncopyable {
63
protected:
64
    /// \brief Constructor.
65
66
67
    ///
    /// It is protected to prevent accidental creation of the abstract base
    /// class.
68
    ClientList() {}
69
public:
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
70
71
    /// \brief Virtual destructor
    virtual ~ClientList() {}
72
    /// \brief Structure holding the (compound) result of find.
73
74
75
76
    ///
    /// As this is read-only structure, we don't bother to create accessors.
    /// Instead, all the member variables are defined as const and can be
    /// accessed directly.
77
    struct FindResult {
78
79
80
81
82
83
84
85
86
87
        /// \brief Internal class for holding a reference.
        ///
        /// This is used to make sure the data source client isn't released
        /// too soon.
        ///
        /// \see life_keeper_;
        class LifeKeeper {
        public:
            virtual ~LifeKeeper() {};
        };
88
89
90
91
        /// \brief Constructor.
        ///
        /// It simply fills in the member variables according to the
        /// parameters. See the member descriptions for their meaning.
92
        FindResult(DataSourceClient* dsrc_client, const ZoneFinderPtr& finder,
93
94
                   bool exact_match,
                   const boost::shared_ptr<LifeKeeper>& life_keeper) :
95
            dsrc_client_(dsrc_client),
96
            finder_(finder),
97
98
            exact_match_(exact_match),
            life_keeper_(life_keeper)
99
        {}
100

101
102
103
        /// \brief Negative answer constructor.
        ///
        /// This conscructs a result for negative answer. Both pointers are
104
        /// NULL, and exact_match_ is false.
105
        FindResult() :
106
            dsrc_client_(NULL),
107
            exact_match_(false)
108
        {}
109

110
111
112
113
        /// \brief Comparison operator.
        ///
        /// It is needed for tests and it might be of some use elsewhere
        /// too.
114
        bool operator ==(const FindResult& other) const {
115
        return (dsrc_client_ == other.dsrc_client_ &&
116
117
                finder_ == other.finder_ &&
                exact_match_ == other.exact_match_);
118
        }
119

120
        /// \brief The found data source client.
121
        ///
122
123
        /// The client of the data source containing the best matching zone.
        /// If no such data source exists, this is NULL pointer.
124
        ///
125
        /// Note that the pointer is valid only as long the ClientList which
126
127
128
        /// returned the pointer is alive and was not reconfigured or you hold
        /// a reference to life_keeper_. The ownership is preserved within the
        /// ClientList.
129
        DataSourceClient* const dsrc_client_;
130

131
132
133
134
        /// \brief The finder for the requested zone.
        ///
        /// This is the finder corresponding to the best matching zone.
        /// This may be NULL even in case the datasrc_ is something
135
        /// else, depending on the find options.
136
        ///
137
        /// \see find
138
        const ZoneFinderPtr finder_;
139

140
141
        /// \brief If the result is an exact match.
        const bool exact_match_;
142
143
144
145
146
147

        /// \brief Something that holds the dsrc_client_ valid.
        ///
        /// As long as you hold the life_keeper_, the dsrc_client_ is
        /// guaranteed to be valid.
        const boost::shared_ptr<LifeKeeper> life_keeper_;
148
    };
149
150

    /// \brief Search for a zone through the data sources.
151
    ///
152
153
    /// This searches the contained data source clients for a one that best
    /// matches the zone name.
154
    ///
155
156
157
158
    /// There are two expected usage scenarios. One is answering queries. In
    /// this case, the zone finder is needed and the best matching superzone
    /// of the searched name is needed. Therefore, the call would look like:
    ///
159
    /// \code FindResult result(list->find(queried_name));
160
    ///   FindResult result(list->find(queried_name));
161
162
163
164
    ///   if (result.datasrc_) {
    ///       createTheAnswer(result.finder_);
    ///   } else {
    ///       createNotAuthAnswer();
165
    /// } \endcode
166
167
168
169
170
171
    ///
    /// The other scenario is manipulating zone data (XfrOut, XfrIn, DDNS,
    /// ...). In this case, the finder itself is not so important. However,
    /// we need an exact match (if we want to manipulate zone data, we must
    /// know exactly, which zone we are about to manipulate). Then the call
    ///
172
    /// \code FindResult result(list->find(zone_name, true, false));
173
    ///   FindResult result(list->find(zone_name, true, false));
174
175
176
    ///   if (result.datasrc_) {
    ///       ZoneUpdaterPtr updater(result.datasrc_->getUpdater(zone_name);
    ///       ...
177
    /// } \endcode
178
    ///
179
    /// \param zone The name of the zone to look for.
180
181
182
183
    /// \param want_exact_match If it is true, it returns only exact matches.
    ///     If the best possible match is partial, a negative result is
    ///     returned instead. It is possible the caller could check it and
    ///     act accordingly if the result would be partial match, but with this
184
    ///     set to true, the find might be actually faster under some
185
    ///     circumstances.
186
    /// \param want_finder If this is false, the finder_ member of FindResult
187
188
189
190
191
192
193
194
195
    ///     might be NULL even if the corresponding data source is found. This
    ///     is because of performance, in some cases the finder is a side
    ///     result of the searching algorithm (therefore asking for it again
    ///     would be a waste), but under other circumstances it is not, so
    ///     providing it when it is not needed would also be wasteful.
    ///
    ///     Other things are never the side effect of searching, therefore the
    ///     caller can get them explicitly (the updater, journal reader and
    ///     iterator).
196
    /// \return A FindResult describing the data source and zone with the
197
    ///     longest match against the zone parameter.
198
199
200
    virtual FindResult find(const dns::Name& zone,
                            bool want_exact_match = false,
                            bool want_finder = true) const = 0;
201
202
};

203
204
205
206
/// \brief Shared pointer to the list.
typedef boost::shared_ptr<ClientList> ClientListPtr;
/// \brief Shared const pointer to the list.
typedef boost::shared_ptr<const ClientList> ConstClientListPtr;
207

208
/// \Concrete implementation of the ClientList, which is constructed based on
209
210
211
///     configuration.
///
/// This is the implementation which is expected to be used in the servers.
212
/// However, it is expected most of the code will use it as the ClientList,
213
/// only the creation is expected to be direct.
214
215
216
///
/// While it is possible to inherit this class, it is not expected to be
/// inherited except for tests.
217
class ConfigurableClientList : public ClientList {
218
public:
219
220
221
    /// \brief Constructor
    ///
    /// \param rrclass For which class the list should work.
222
223
    ConfigurableClientList(const isc::dns::RRClass& rrclass);

224
225
226
    /// \brief Destructor
    virtual ~ConfigurableClientList();

227
228
229
230
231
    /// \brief Exception thrown when there's an error in configuration.
    class ConfigurationError : public Exception {
    public:
        ConfigurationError(const char* file, size_t line, const char* what) :
            Exception(file, line, what)
232
        {}
233
    };
234

235
    /// \brief Sets the configuration.
236
    ///
237
238
239
    /// This fills the ClientList with data source clients corresponding to the
    /// configuration. The data source clients are newly created or recycled
    /// from previous configuration.
240
241
242
    ///
    /// If any error is detected, an exception is thrown and the current
    /// configuration is preserved.
243
244
245
246
247
248
249
    ///
    /// \param configuration The JSON element describing the configuration to
    ///     use.
    /// \param allow_cache If it is true, the 'cache' option of the
    ///     configuration is used and some zones are cached into an In-Memory
    ///     data source according to it. If it is false, it is ignored and
    ///     no In-Memory data sources are created.
250
251
    /// \throw DataSourceError if there's a problem creating a data source
    ///     client.
252
253
    /// \throw ConfigurationError if the configuration is invalid in some
    ///     sense.
254
    /// \throw BadValue if configuration is NULL
255
256
257
258
259
    /// \throw Unexpected if something misbehaves (like the data source
    ///     returning NULL iterator).
    /// \throw NotImplemented if the auto-detection of list of zones is
    ///     needed.
    /// \throw Whatever is propagated from within the data source.
260
261
262
263
264
265
266
267
268
269
    void configure(const isc::data::ConstElementPtr& configuration,
                   bool allow_cache);

    /// \brief Returns the currently active configuration.
    ///
    /// In case configure was not called yet, it returns an empty
    /// list, which corresponds to the default content.
    const isc::data::ConstElementPtr& getConfiguration() const {
        return (configuration_);
    }
270

271
272
273
    /// \brief Result of the reload() method.
    enum ReloadResult {
        CACHE_DISABLED,     ///< The cache is not enabled in this list.
274
275
        ZONE_NOT_CACHED,    ///< Zone is served directly, not from cache.
        ZONE_NOT_FOUND,     ///< Zone does not exist or not cached.
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
        ZONE_RELOADED       ///< The zone was successfully reloaded.
    };

    /// \brief Reloads a cached zone.
    ///
    /// This method finds a zone which is loaded into a cache and reloads it.
    /// This may be used to renew the cache when the underlying data source
    /// changes.
    ///
    /// \param zone The origin of the zone to reload.
    /// \return A status if the command worked.
    /// \throw DataSourceError or anything else that the data source
    ///      containing the zone might throw is propagated.
    /// \throw DataSourceError if something unexpected happens, like when
    ///      the original data source no longer contains the cached zone.
    ReloadResult reload(const dns::Name& zone);
292

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
    /// \brief Return value of getCachedZoneWriter()
    ///
    /// A pair containing status and the zone writer, for the
    /// getCachedZoneWriter() method.
    typedef std::pair<ReloadResult, boost::shared_ptr<memory::ZoneWriter> >
        ZoneWriterPair;

    /// \brief Return a zone writer that can be used to reload a zone.
    ///
    /// This looks up a cached copy of zone and returns the ZoneWriter
    /// that can be used to reload the content of the zone. This can
    /// be used instead of reload() -- reload() works synchronously, which
    /// is not what is needed every time.
    ///
    /// \param zone The origin of the zone to reload.
    /// \return The result has two parts. The first one is a status describing
    ///     if it worked or not (and in case it didn't, also why). If the status
    ///     is ZONE_RELOADED, the second part contains a shared pointer to the
    ///     writer. If the status is anything else, the second part is NULL.
    /// \throw DataSourceError or anything else that the data source
    ///      containing the zone might throw is propagated.
    /// \throw DataSourceError if something unexpected happens, like when
    ///      the original data source no longer contains the cached zone.
    ZoneWriterPair getCachedZoneWriter(const dns::Name& zone);

318
    /// \brief Implementation of the ClientList::find.
319
    virtual FindResult find(const dns::Name& zone,
320
321
                            bool want_exact_match = false,
                            bool want_finder = true) const;
322

323
    /// \brief This holds one data source client and corresponding information.
324
325
    ///
    /// \todo The content yet to be defined.
326
    struct DataSourceInfo {
327
        // Plays a role of default constructor too (for vector)
328
329
330
        DataSourceInfo(const dns::RRClass& rrclass,
                       util::MemorySegment& mem_sgmt,
                       bool has_cache = false);
331
        DataSourceInfo(DataSourceClient* data_src_client,
332
                       const DataSourceClientContainerPtr& container,
333
334
                       bool has_cache, const dns::RRClass& rrclass,
                       util::MemorySegment& mem_sgmt);
335
        DataSourceClient* data_src_client_;
336
        DataSourceClientContainerPtr container_;
337
338
339
340
341
342
343

        // Accessor to cache_ in the form of DataSourceClient, hiding
        // the existence of InMemoryClient as much as possible.  We should
        // really consider cleaner abstraction, but for now it works.
        // This is also only intended to be used in auth unit tests right now.
        // No other applications or tests may use it.
        const DataSourceClient* getCacheClient() const;
344
        boost::shared_ptr<memory::InMemoryClient> cache_;
345
    };
346

347
348
    /// \brief The collection of data sources.
    typedef std::vector<DataSourceInfo> DataSources;
349

350
351
352
353
354
    /// \brief Convenience type alias.
    ///
    /// \see getDataSource
    typedef std::pair<DataSourceClient*, DataSourceClientContainerPtr>
        DataSourcePair;
355

356
    /// \brief Create a data source client of given type and configuration.
357
358
359
360
    ///
    /// This is a thin wrapper around the DataSourceClientContainer
    /// constructor. The function is here to make it possible for tests
    /// to replace the DataSourceClientContainer with something else.
361
362
    /// Also, derived classes could want to create the data source clients
    /// in a different way, though inheriting this class is not recommended.
363
364
    ///
    /// The parameters are the same as of the constructor.
365
    /// \return Pair containing both the data source client and the container.
366
    ///     The container might be NULL in the derived class, it is
367
    ///     only stored so the data source client is properly destroyed when
368
    ///     not needed. However, in such case, it is the caller's
369
    ///     responsibility to ensure the data source client is deleted when
370
    ///     needed.
371
372
373
    virtual DataSourcePair getDataSourceClient(const std::string& type,
                                               const data::ConstElementPtr&
                                               configuration);
374
public:
375
    /// \brief Access to the data source clients.
376
    ///
377
378
379
380
    /// It can be used to examine the loaded list of data sources clients
    /// directly. It is not known if it is of any use other than testing, but
    /// it might be, so it is just made public (there's no real reason to
    /// hide it).
381
    const DataSources& getDataSources() const { return (data_sources_); }
382
private:
383
384
385
386
387
388
389
390
    struct MutableResult;
    /// \brief Internal implementation of find.
    ///
    /// The class itself needs to do some internal searches in other methods,
    /// so the implementation is shared.
    ///
    /// The result is returned as parameter because MutableResult is not
    /// defined in the header file.
391
392
393
394
    ///
    /// If there's no match, the result is not modified. Therefore, this
    /// expects to get a fresh result object each time it is called, not
    /// to reuse it.
395
396
    void findInternal(MutableResult& result, const dns::Name& name,
                      bool want_exact_match, bool want_finder) const;
397
    const isc::dns::RRClass rrclass_;
398
399
400
401
402
403
404

    /// \brief Memory segment for in-memory cache.
    ///
    /// Note that this must be placed before data_sources_ so it won't be
    /// destroyed before the built objects in the destructor.
    boost::scoped_ptr<util::MemorySegment> mem_sgmt_;

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
405
406
    /// \brief Currently active configuration.
    isc::data::ConstElementPtr configuration_;
407

408
409
    /// \brief The last set value of allow_cache.
    bool allow_cache_;
410
411
412
413
414
415
416
417

protected:
    /// \brief The data sources held here.
    ///
    /// All our data sources are stored here. It is protected to let the
    /// tests in. You should consider it private if you ever want to
    /// derive this class (which is not really recommended anyway).
    DataSources data_sources_;
418
419
420
421
422
423
};

} // namespace datasrc
} // namespace isc

#endif // DATASRC_CONTAINER_H