Commit f38d24a7 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[2203] refactoring 2nd step: configurator can now be a separate object.

i.e., it's not a singleton any more.
testReconfigure() method isn't needed any more because it doesn't hold
CC session internally.
DatasrcConfiguratorTest.initialization test currently fails and is
disabled for now.  The plan is to make the class completely stateless,
at which point we don't even have to think about initialization or cleanup,
and then the test will be able to be removed.
parent da22b377
......@@ -81,6 +81,7 @@ protected:
QueryBenchMark(const BenchQueries& queries, Message& query_message,
OutputBuffer& buffer) :
server_(new AuthSrv(xfrout_client, ddns_forwarder)),
datasrc_configurator_(server_.get()),
queries_(queries),
query_message_(query_message),
buffer_(buffer),
......@@ -109,6 +110,7 @@ private:
MockSocketSessionForwarder ddns_forwarder;
protected:
AuthSrvPtr server_;
DataSourceConfigurator datasrc_configurator_;
private:
const BenchQueries& queries_;
Message& query_message_;
......@@ -125,8 +127,7 @@ public:
OutputBuffer& buffer) :
QueryBenchMark(queries, query_message, buffer)
{
DataSourceConfigurator::testReconfigure(
server_.get(),
datasrc_configurator_.reconfigure(
Element::fromJSON("{\"IN\":"
" [{\"type\": \"sqlite3\","
" \"params\": {"
......@@ -139,13 +140,12 @@ class MemoryQueryBenchMark : public QueryBenchMark {
public:
MemoryQueryBenchMark(const char* const zone_file,
const char* const zone_origin,
const BenchQueries& queries,
Message& query_message,
OutputBuffer& buffer) :
const BenchQueries& queries,
Message& query_message,
OutputBuffer& buffer) :
QueryBenchMark(queries, query_message, buffer)
{
DataSourceConfigurator::testReconfigure(
server_.get(),
datasrc_configurator_.reconfigure(
Element::fromJSON("{\"IN\":"
" [{\"type\": \"MasterFiles\","
" \"cache-enable\": true, "
......
......@@ -30,32 +30,25 @@
/// keep the local copy of data source clients in the list in the authoritative
/// server.
///
/// The class is slightly unusual. Due to some technical limitations, the hook
/// needs to be static method. Therefore it is not possible to create instances
/// of the class.
///
/// Also, the class is a template. This is simply because of easier testing.
/// You don't need to pay attention to it, use the DataSourceConfigurator
/// type alias instead.
template<class Server, class List>
class DataSourceConfiguratorGeneric {
private:
/// \brief Disallow creation of instances
DataSourceConfiguratorGeneric();
/// \brief Internal method to hook into the ModuleCCSession
Server* server_;
typedef boost::shared_ptr<List> ListPtr;
public:
/// \brief Constructor.
///
/// It simply calls reconfigure.
static void reconfigureInternal(const std::string&,
isc::data::ConstElementPtr config,
const isc::config::ConfigData&)
{
if (config->contains("classes")) {
reconfigure(config->get("classes"));
/// \throw isc::InvalidParameter if server is NULL
/// \param server The server to configure.
DataSourceConfiguratorGeneric(Server* server) : server_(server) {
if (server == NULL) {
isc_throw(isc::InvalidParameter, "The server must not be NULL");
}
}
static Server* server_;
typedef boost::shared_ptr<List> ListPtr;
public:
/// \brief Initializes the class.
///
/// This configures which session and server should be used.
......@@ -66,22 +59,13 @@ public:
/// session dies, otherwise it might access them after they
/// are destroyed.
///
/// \param session The session to hook into and to access the configuration
/// through.
/// \param server It is the server to configure.
/// \throw isc::InvalidOperation if this is called when already initialized.
/// \throw isc::InvalidOperation if this is called when already
/// initialized.
/// \throw isc::InvalidParameter if any of the parameters is NULL
/// \throw isc::config::ModuleCCError if the remote configuration is not
/// available for some reason.
static void init(Server* server) {
if (server == NULL) {
isc_throw(isc::InvalidParameter, "The server must not be NULL");
}
if (server_ != NULL) {
isc_throw(isc::InvalidOperation,
"The configurator is already initialized");
}
server_ = server;
void init() {
}
/// \brief Deinitializes the class.
......@@ -91,7 +75,7 @@ public:
///
/// This can be called even if it is not initialized currently. You
/// can initialize it again after this.
static void cleanup() {
void cleanup() {
server_ = NULL;
}
......@@ -105,7 +89,7 @@ public:
/// \param config The configuration value to parse. It is in the form
/// as an update from the config manager.
/// \throw InvalidOperation if it is called when not initialized.
static void reconfigure(const isc::data::ConstElementPtr& config) {
void reconfigure(const isc::data::ConstElementPtr& config) {
if (server_ == NULL) {
isc_throw(isc::InvalidOperation,
"Can't reconfigure while not initialized by init()");
......@@ -171,38 +155,8 @@ public:
throw;
}
}
/// \brief Version of reconfigure for easier testing.
///
/// This method can be used to reconfigure a server without first
/// initializing the configurator. This does not need a session.
/// Otherwise, it acts the same as reconfigure.
///
/// This is not meant for production code. Do not use there.
///
/// \param server The server to configure.
/// \param config The config to use.
/// \throw isc::InvalidOperation if the configurator is initialized.
/// \throw anything that reconfigure does.
static void testReconfigure(Server* server,
const isc::data::ConstElementPtr& config)
{
if (server_ != NULL) {
isc_throw(isc::InvalidOperation, "Currently initialized.");
}
try {
server_ = server;
reconfigure(config);
server_ = NULL;
} catch (...) {
server_ = NULL;
throw;
}
}
};
template<class Server, class List>
Server* DataSourceConfiguratorGeneric<Server, List>::server_(NULL);
/// \brief Concrete version of DataSourceConfiguratorGeneric for the
/// use in authoritative server.
typedef DataSourceConfiguratorGeneric<AuthSrv,
......
......@@ -14,17 +14,6 @@
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <cassert>
#include <iostream>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
......@@ -52,6 +41,20 @@
#include <server_common/keyring.h>
#include <server_common/socket_request.h>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <cassert>
#include <iostream>
using namespace std;
using namespace isc::asiodns;
using namespace isc::asiolink;
......@@ -84,12 +87,12 @@ my_command_handler(const string& command, ConstElementPtr args) {
}
void
datasrcConfigHandler(const std::string&,
datasrcConfigHandler(DataSourceConfigurator* configurator, const std::string&,
isc::data::ConstElementPtr config,
const isc::config::ConfigData&)
{
if (config->contains("classes")) {
DataSourceConfigurator::reconfigure(config->get("classes"));
configurator->reconfigure(config->get("classes"));
}
}
......@@ -137,6 +140,7 @@ main(int argc, char* argv[]) {
ModuleCCSession* config_session = NULL;
XfroutClient xfrout_client(getXfroutSocketPath());
SocketSessionForwarder ddns_forwarder(getDDNSSocketPath());
boost::scoped_ptr<DataSourceConfigurator> datasrc_configurator;
try {
string specfile;
if (getenv("B10_FROM_BUILD")) {
......@@ -202,13 +206,17 @@ main(int argc, char* argv[]) {
auth_server->setTSIGKeyRing(&isc::server_common::keyring);
// Start the data source configuration
DataSourceConfigurator::init(auth_server);
config_session->addRemoteConfig("data_sources", datasrcConfigHandler,
datasrc_configurator.reset(new DataSourceConfigurator(auth_server));
config_session->addRemoteConfig("data_sources",
boost::bind(datasrcConfigHandler,
datasrc_configurator.get(),
_1, _2, _3),
false);
// HACK: The default is not passed to the handler. This one will
// get the default (or, current value). Further updates will work
// the usual way.
DataSourceConfigurator::reconfigure(
datasrc_configurator->reconfigure(
config_session->getRemoteConfigValue("data_sources", "classes"));
// Now start asynchronous read.
......@@ -233,8 +241,9 @@ main(int argc, char* argv[]) {
xfrin_session->disconnect();
}
//DataSourceConfigurator::cleanup();
config_session->removeRemoteConfig("data_sources");
if (datasrc_configurator) {
config_session->removeRemoteConfig("data_sources");
}
delete xfrin_session;
delete config_session;
delete cc_session;
......
......@@ -98,7 +98,8 @@ protected:
// The empty string is expected value of the parameter of
// requestSocket, not the app_name (there's no fallback, it checks
// the empty string is passed).
sock_requestor_(dnss_, address_store_, 53210, "")
sock_requestor_(dnss_, address_store_, 53210, ""),
datasrc_configurator_(&server)
{
server.setDNSService(dnss_);
server.setXfrinSession(&notify_session);
......@@ -183,6 +184,7 @@ protected:
vector<uint8_t> response_data;
AddressList address_store_;
TestSocketRequestor sock_requestor_;
DataSourceConfigurator datasrc_configurator_;
};
// A helper function that builds a response to version.bind/TXT/CH that
......@@ -722,17 +724,21 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
}
void
updateDatabase(AuthSrv* server, const char* params) {
updateDatabase(DataSourceConfigurator& datasrc_configurator,
const char* params)
{
const ConstElementPtr config(Element::fromJSON("{"
"\"IN\": [{"
" \"type\": \"sqlite3\","
" \"params\": " + string(params) +
"}]}"));
DataSourceConfigurator::testReconfigure(server, config);
datasrc_configurator.reconfigure(config);
}
void
updateInMemory(AuthSrv* server, const char* origin, const char* filename) {
updateInMemory(DataSourceConfigurator& datasrc_configurator,
const char* origin, const char* filename)
{
const ConstElementPtr config(Element::fromJSON("{"
"\"IN\": [{"
" \"type\": \"MasterFiles\","
......@@ -745,17 +751,17 @@ updateInMemory(AuthSrv* server, const char* origin, const char* filename) {
" \"type\": \"static\","
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
"}]}"));
DataSourceConfigurator::testReconfigure(server, config);
datasrc_configurator.reconfigure(config);
}
void
updateBuiltin(AuthSrv* server) {
updateBuiltin(DataSourceConfigurator& datasrc_configurator) {
const ConstElementPtr config(Element::fromJSON("{"
"\"CH\": [{"
" \"type\": \"static\","
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
"}]}"));
DataSourceConfigurator::testReconfigure(server, config);
datasrc_configurator.reconfigure(config);
}
// Try giving the server a TSIG signed request and see it can anwer signed as
......@@ -766,7 +772,7 @@ TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin
TEST_F(AuthSrvTest, TSIGSigned) {
#endif
// Prepare key, the client message, etc
updateBuiltin(&server);
updateBuiltin(datasrc_configurator_);
const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
TSIGContext context(key);
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
......@@ -814,7 +820,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) {
#else
TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
#endif
updateBuiltin(&server);
updateBuiltin(datasrc_configurator_);
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("VERSION.BIND."),
RRClass::CH(), RRType::TXT());
......@@ -846,7 +852,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQuery) {
#else
TEST_F(AuthSrvTest, builtInQuery) {
#endif
updateBuiltin(&server);
updateBuiltin(datasrc_configurator_);
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("VERSION.BIND."),
RRClass::CH(), RRType::TXT());
......@@ -867,7 +873,7 @@ TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin
#else
TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin
#endif
updateBuiltin(&server);
updateBuiltin(datasrc_configurator_);
createDataFromFile("iquery_fromWire.wire");
(*server.getDNSLookupProvider())(*io_message, parse_message,
response_message,
......@@ -889,7 +895,7 @@ TEST_F(AuthSrvTest, DISABLED_updateConfig) {
#else
TEST_F(AuthSrvTest, updateConfig) {
#endif
updateDatabase(&server, CONFIG_TESTDB);
updateDatabase(datasrc_configurator_, CONFIG_TESTDB);
// query for existent data in the installed data source. The resulting
// response should have the AA flag on, and have an RR in each answer
......@@ -907,7 +913,7 @@ TEST_F(AuthSrvTest, DISABLED_datasourceFail) {
#else
TEST_F(AuthSrvTest, datasourceFail) {
#endif
updateDatabase(&server, CONFIG_TESTDB);
updateDatabase(datasrc_configurator_, CONFIG_TESTDB);
// This query will hit a corrupted entry of the data source (the zoneload
// tool and the data source itself naively accept it). This will result
......@@ -927,10 +933,10 @@ TEST_F(AuthSrvTest, DISABLED_updateConfigFail) {
TEST_F(AuthSrvTest, updateConfigFail) {
#endif
// First, load a valid data source.
updateDatabase(&server, CONFIG_TESTDB);
updateDatabase(datasrc_configurator_, CONFIG_TESTDB);
// Next, try to update it with a non-existent one. This should fail.
EXPECT_THROW(updateDatabase(&server, BADCONFIG_TESTDB),
EXPECT_THROW(updateDatabase(datasrc_configurator_, BADCONFIG_TESTDB),
isc::datasrc::DataSourceError);
// The original data source should still exist.
......@@ -953,7 +959,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
" \"params\": {},"
" \"cache-enable\": true"
"}]}"));
DataSourceConfigurator::testReconfigure(&server, config);
datasrc_configurator_.reconfigure(config);
// after successful configuration, we should have one (with empty zoneset).
// The memory data source is empty, should return REFUSED rcode.
......@@ -974,7 +980,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
// query handler class, and confirm it returns no error and a non empty
// answer section. Detailed examination on the response content
// for various types of queries are tested in the query tests.
updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE);
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
server.processMessage(*io_message, *parse_message, *response_obuffer,
......@@ -993,7 +999,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
// Similar to the previous test, but the query has the DO bit on.
// The response should contain RRSIGs, and should have more RRs than
// the previous case.
updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE);
createDataFromFile("nsec3query_fromWire.wire");
server.processMessage(*io_message, *parse_message, *response_obuffer,
......@@ -1013,7 +1019,7 @@ TEST_F(AuthSrvTest,
)
{
// Set up the in-memory
updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE);
// This shouldn't affect the result of class CH query
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
......@@ -1425,7 +1431,7 @@ TEST_F(AuthSrvTest,
)
{
// Set real inmem client to proxy
updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE);
{
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
......@@ -1450,10 +1456,11 @@ TEST_F(AuthSrvTest,
// If non null rrset is given, it will be passed to the proxy so it can
// return some faked response.
void
setupThrow(AuthSrv* server, ThrowWhen throw_when, bool isc_exception,
setupThrow(AuthSrv* server, DataSourceConfigurator& datasrc_configurator,
ThrowWhen throw_when, bool isc_exception,
ConstRRsetPtr rrset = ConstRRsetPtr())
{
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
updateInMemory(datasrc_configurator, "example.", CONFIG_INMEMORY_EXAMPLE);
isc::util::thread::Mutex::Locker locker(server->getClientListMutex());
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
......@@ -1482,11 +1489,11 @@ TEST_F(AuthSrvTest,
RRClass::IN(), RRType::TXT());
for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
createRequestPacket(request_message, IPPROTO_UDP);
setupThrow(&server, *when, true);
setupThrow(&server, datasrc_configurator_, *when, true);
processAndCheckSERVFAIL();
// To be sure, check same for non-isc-exceptions
createRequestPacket(request_message, IPPROTO_UDP);
setupThrow(&server, *when, false);
setupThrow(&server, datasrc_configurator_, *when, false);
processAndCheckSERVFAIL();
}
}
......@@ -1502,7 +1509,7 @@ TEST_F(AuthSrvTest,
)
{
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
setupThrow(&server, THROW_AT_GET_CLASS, true);
setupThrow(&server, datasrc_configurator_, THROW_AT_GET_CLASS, true);
// getClass is not called so it should just answer
server.processMessage(*io_message, *parse_message, *response_obuffer,
......@@ -1526,7 +1533,7 @@ TEST_F(AuthSrvTest,
ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"),
RRClass::IN(), RRType::TXT(),
RRTTL(0)));
setupThrow(&server, THROW_NEVER, true, empty_rrset);
setupThrow(&server, datasrc_configurator_, THROW_NEVER, true, empty_rrset);
// Repeat the query processing two times. Due to the faked RRset,
// toWire() should throw, and it should result in SERVFAIL.
......
......@@ -64,6 +64,7 @@ class AuthCommandTest : public ::testing::Test {
protected:
AuthCommandTest() :
server_(xfrout_, ddns_forwarder_),
datasrc_configurator_(&server_),
rcode_(-1),
expect_rcode_(0),
itimer_(server_.getIOService())
......@@ -77,6 +78,7 @@ protected:
MockXfroutClient xfrout_;
MockSocketSessionForwarder ddns_forwarder_;
AuthSrv server_;
DataSourceConfigurator datasrc_configurator_;
ConstElementPtr result_;
// The shutdown command parameter
ConstElementPtr param_;
......@@ -190,7 +192,7 @@ zoneChecks(AuthSrv& server) {
}
void
configureZones(AuthSrv& server) {
configureZones(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) {
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
......@@ -208,7 +210,7 @@ configureZones(AuthSrv& server) {
" \"cache-enable\": true"
"}]}"));
DataSourceConfigurator::testReconfigure(&server, config);
datasrc_configurator.reconfigure(config);
zoneChecks(server);
}
......@@ -234,7 +236,7 @@ newZoneChecks(AuthSrv& server) {
}
TEST_F(AuthCommandTest, loadZone) {
configureZones(server_);
configureZones(server_, datasrc_configurator_);
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
"/test1-new.zone.in "
......@@ -271,7 +273,7 @@ TEST_F(AuthCommandTest,
" \"cache-enable\": true,"
" \"cache-zones\": [\"example.org\"]"
"}]}"));
DataSourceConfigurator::testReconfigure(&server_, config);
datasrc_configurator_.reconfigure(config);
{
isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
......@@ -335,7 +337,7 @@ TEST_F(AuthCommandTest,
" \"cache-enable\": true,"
" \"cache-zones\": [\"example.com\"]"
"}]}"));
EXPECT_THROW(DataSourceConfigurator::testReconfigure(&server_, config2),
EXPECT_THROW(datasrc_configurator_.reconfigure(config2),
ConfigurableClientList::ConfigurationError);
result_ = execAuthServerCommand(server_, "loadzone",
......@@ -350,7 +352,7 @@ TEST_F(AuthCommandTest,
}
TEST_F(AuthCommandTest, loadBrokenZone) {
configureZones(server_);
configureZones(server_, datasrc_configurator_);
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
"/test1-broken.zone.in "
......@@ -363,7 +365,7 @@ TEST_F(AuthCommandTest, loadBrokenZone) {
}
TEST_F(AuthCommandTest, loadUnreadableZone) {
configureZones(server_);
configureZones(server_, datasrc_configurator_);
// install the zone file as unreadable
ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
......@@ -386,7 +388,7 @@ TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
}
TEST_F(AuthCommandTest, loadZoneInvalidParams) {
configureZones(server_);
configureZones(server_, datasrc_configurator_);
// null arg
result_ = execAuthServerCommand(server_, "loadzone", ElementPtr());
......
......@@ -19,9 +19,12 @@
#include <util/threads/lock.h>
#include <gtest/gtest.h>
#include <memory>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <memory>
using namespace isc;
using namespace isc::cc;
using namespace isc::config;
......@@ -63,12 +66,12 @@ typedef DataSourceConfiguratorGeneric<DatasrcConfiguratorTest,
FakeList> Configurator;
void
datasrcConfigHandler(const std::string&,
datasrcConfigHandler(Configurator* configurator, const std::string&,
isc::data::ConstElementPtr config,
const isc::config::ConfigData&)
{
if (config->contains("classes")) {
Configurator::reconfigure(config->get("classes"));
configurator->reconfigure(config->get("classes"));
}
}
......@@ -99,6 +102,7 @@ protected:
DatasrcConfiguratorTest() :
session(ElementPtr(new ListElement), ElementPtr(new ListElement),
ElementPtr(new ListElement)),
configurator_(this),
specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec")
{
initSession();
......@@ -111,7 +115,6 @@ protected:
void TearDown() {
// Make sure no matter what we did, it is cleaned up.
mccs->removeRemoteConfig("data_sources");
Configurator::cleanup();
}
void init(const ElementPtr& config = ElementPtr()) {
session.getMessages()->
......@@ -125,8 +128,9 @@ protected:
session.getMessages()->
add(createAnswer(0, ElementPtr(new MapElement)));
}
Configurator::init(this);
mccs->addRemoteConfig("data_sources", datasrcConfigHandler, false);
mccs->addRemoteConfig("data_sources",
boost::bind(datasrcConfigHandler, &configurator_,
_1, _2, _3), false);
}
void SetUp() {
init();
......@@ -150,6 +154,7 @@ protected:
}
FakeSession session;
auto_ptr<ModuleCCSession> mccs;
Configurator configurator_;
const string specfile;
std::map<RRClass, ListPtr> lists_;
string log_;
......@@ -157,20 +162,17 @@ protected: