bool ossimGmlSupportData::getImageGeometry( ossimKeywordlist& geomKwl ) const
{
   bool success = true;
   
   if ( m_xmlDocument.valid() )
   {
      vector< ossimRefPtr<ossimXmlNode> > xml_nodes;
      bool gotSensorImage    = false;
      bool gotRectifiedImage = false;
      ossim_uint32 pcsCodeGrid = 32767; // only applies to rectified

      // Check the GMLJP2CoverageCollection attributes for the default namespace.
      ossimString defaultNamespaceStr( "" );
      ossimString xpath_root = "/gmljp2:GMLJP2CoverageCollection";
      xml_nodes.clear();
      m_xmlDocument->findNodes( xpath_root, xml_nodes );
      if ( xml_nodes.size() == 0 ) 
      {
          // check if the default namespace is gmljp2
          xpath_root = "/GMLJP2CoverageCollection";
          m_xmlDocument->findNodes( xpath_root, xml_nodes );
      }
      if ( xml_nodes.size() >= 1 )
      {
         const ossimString defaultNamespaceIdentifierStr( "xmlns" );
         ossimString defaultNamespacePrependStr = defaultNamespaceIdentifierStr + ":";

         const ossimRefPtr<ossimXmlAttribute> defaultNamespaceAttribute = xml_nodes[0]->findAttribute( defaultNamespaceIdentifierStr );
         ossimString defaultNamespaceSettingStr = defaultNamespaceAttribute->getValue();

         // search for the attribute value in the other attributes
         const ossimXmlNode::AttributeListType& attributeList = xml_nodes[0]->getAttributes();
         size_t nAttributes = attributeList.size();
         for ( size_t i=0; i<nAttributes; ++i )
         {
            const ossimRefPtr<ossimXmlAttribute> attribute = attributeList[i];
         
            const ossimString& attribute_name  = attribute->getName();
            const ossimString& attribute_value = attribute->getValue();

            if ( attribute_name  != defaultNamespaceIdentifierStr && 
                 attribute_value == defaultNamespaceSettingStr )
            {
                defaultNamespaceStr = attribute_name.after( defaultNamespacePrependStr );
                defaultNamespaceStr += ":";
            }
         }
      }
       
      // Check for a sensor image
      ossimString xpath0 = "/gmljp2:GMLJP2CoverageCollection/gmljp2:featureMember/gmljp2:GMLJP2ReferenceableGridCoverage/gml:domainSet/gmlcov:ReferenceableGridBySensorModel";
      xpath0 = xpath0.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
      xml_nodes.clear();
      m_xmlDocument->findNodes( xpath0, xml_nodes );
      if ( xml_nodes.size() >= 1 )
      {
         // we've got a sensor model image
         gotSensorImage = true;
      }
      else
      {
         const ossimString srsNameStr( "srsName" );
         ossimString pcsCodeDefinitionStr( "http://www.opengis.net/def/crs/EPSG/0/" );

         xpath0 = "/gmljp2:GMLJP2CoverageCollection/gmljp2:featureMember/gmljp2:GMLJP2RectifiedGridCoverage/gml:domainSet/gml:RectifiedGrid";
         xpath0 = xpath0.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
         xml_nodes.clear();
         m_xmlDocument->findNodes( xpath0, xml_nodes );
         if ( xml_nodes.size() >= 1 )
         {
            // we've got a rectified image
            gotRectifiedImage = true;

            const ossimRefPtr<ossimXmlAttribute> hrefAttribute = xml_nodes[0]->findAttribute( srsNameStr );
            const ossimString& originSrsName = hrefAttribute->getValue();
            ossimString pcsCodeGridStr = originSrsName.after( pcsCodeDefinitionStr.string() );
            pcsCodeGrid = pcsCodeGridStr.toUInt32();
            if ( pcsCodeGrid != 32767 )
            {
               //---
               // The ossimEpsgProjectionFactory will not pick up the origin latitude if code is
               // 4326 (geographic) so we use the projection name; else, the origin_latitude will
               // always be 0.  This is so the gsd comes out correct for scale.
               //---
               if ( pcsCodeGrid != 4326 ) // map projection
               {
                  // Add the pcs code.
                  geomKwl.add( ossimKeywordNames::PCS_CODE_KW,
                               pcsCodeGridStr.c_str() );
               }
               else // geographic
               {
                  geomKwl.add( ossimKeywordNames::TYPE_KW, 
                               ossimString( "ossimEquDistCylProjection" ) );
               }
            }
         }
      }

      /* Number of lines & samples, for either sensor or rectified imagery */

      ossimString xpath_limits_low  = "/gml:limits/gml:GridEnvelope/gml:low";
      ossimString xpath_limits_high = "/gml:limits/gml:GridEnvelope/gml:high";

      xpath_limits_low  = xpath_limits_low.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
      xpath_limits_high = xpath_limits_high.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );

      bool gotLow = false;
      ossim_int32 lowX, lowY;
      ossimString xpath = xpath0 + xpath_limits_low;
      xml_nodes.clear();
      m_xmlDocument->findNodes( xpath, xml_nodes );
      if ( xml_nodes.size() == 1 )
      {
         const ossimString& lowerCorner = xml_nodes[0]->getText();
         size_t spacePos = lowerCorner.find( ' ' );
         ossimString lowerXString = lowerCorner.beforePos( spacePos );
         ossimString lowerYString = lowerCorner.afterPos ( spacePos );
         lowX = lowerXString.toInt32();
         lowY = lowerYString.toInt32();
         gotLow = true;
      }

      bool gotHigh = false;
      ossim_int32 highX = 0;
      ossim_int32 highY = 0;
      xpath = xpath0 + xpath_limits_high;
      xml_nodes.clear();
      m_xmlDocument->findNodes( xpath, xml_nodes );
      if ( xml_nodes.size() == 1 )
      {
         const ossimString& higherCorner = xml_nodes[0]->getText();
         size_t spacePos = higherCorner.find( ' ' );
         ossimString higherXString = higherCorner.beforePos( spacePos );
         ossimString higherYString = higherCorner.afterPos ( spacePos );
         highX = higherXString.toInt32();
         highY = higherYString.toInt32();
         gotHigh = true;
      }

      if ( gotHigh && gotLow )
      {
         geomKwl.add( ossimKeywordNames::NUMBER_LINES_KW,   highY - lowY + 1 );
         geomKwl.add( ossimKeywordNames::NUMBER_SAMPLES_KW, highX - lowX + 1 );
      }

      if ( gotSensorImage )
      {
         const ossimString hrefStr( "xlink:href" );
         const ossimString codeSpaceStr( "codeSpace" );

         ossimString sensorModelHref( "" );
         ossimString xpath_sensor_model = "/gmlcov:sensorModel";
         xpath_sensor_model = xpath_sensor_model.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
         xpath = xpath0 + xpath_sensor_model;
         xml_nodes.clear();
         m_xmlDocument->findNodes( xpath, xml_nodes );
         if ( xml_nodes.size() == 1 )
         {
            const ossimRefPtr<ossimXmlAttribute> hrefAttribute = xml_nodes[0]->findAttribute( hrefStr );
            sensorModelHref = hrefAttribute->getValue();
         }

         ossimString sensorInstanceHref( "" );
         ossimString xpath_sensor_typeOf = "/gmlcov:sensorInstance/sml:SimpleProcess/sml:typeOf";
         xpath_sensor_typeOf = xpath_sensor_typeOf.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
         xpath = xpath0 + xpath_sensor_typeOf;
         xml_nodes.clear();
         m_xmlDocument->findNodes( xpath, xml_nodes );
         if ( xml_nodes.size() == 1 )
         {
            const ossimRefPtr<ossimXmlAttribute> hrefAttribute = xml_nodes[0]->findAttribute( hrefStr );
            sensorInstanceHref = hrefAttribute->getValue();
         }

         ossimSensorModel* sensor_model = 0;
         ossimString xpath_sensor_name = "/gmlcov:sensorInstance/sml:SimpleProcess/gml:name";
         xpath_sensor_name = xpath_sensor_name.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
         xpath = xpath0 + xpath_sensor_name;
         xml_nodes.clear();
         m_xmlDocument->findNodes( xpath, xml_nodes );
         int nSensorNames = (int)xml_nodes.size();
         for ( int i=0; i<nSensorNames; ++i )
         {
            const ossimString& sensorName = xml_nodes[i]->getText();

            ossimProjectionFactoryRegistry* registry = ossimProjectionFactoryRegistry::instance();
            ossimProjection* proj = registry->createProjection( sensorName );

            // Is it a sensor model ?
            sensor_model = dynamic_cast<ossimSensorModel*>( proj );
            if ( sensor_model != 0 )
            {
               geomKwl.add( ossimKeywordNames::TYPE_KW, sensorName.c_str() );
               break;
            }
         }
         if ( sensor_model == 0 )
         {
            // Add debug message
            return false;
         }

         // Check if the sensor instance is typeOf the sensor model
         if ( sensorModelHref == sensorInstanceHref )
         {
            const ossimString refStr( "ref" );

            /* sml:setValue */
            ossimString xpath_setValue = "/gmlcov:sensorInstance/sml:SimpleProcess/sml:configuration/sml:Settings/sml:setValue";
            xpath_setValue = xpath_setValue.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
            xpath = xpath0 + xpath_setValue;
            xml_nodes.clear();
            m_xmlDocument->findNodes( xpath, xml_nodes );
            size_t nXmlNodes = xml_nodes.size();
            for( size_t i=0; i<nXmlNodes; ++i )
            {
               const ossimString& elementValue = xml_nodes[i]->getText();

               const ossimRefPtr<ossimXmlAttribute> refAttribute = xml_nodes[i]->findAttribute( refStr );
               const ossimString& settingsRef = refAttribute->getValue();

               bool successSetValue = sensor_model->getImageGeometry( settingsRef, elementValue, geomKwl );
               success &= successSetValue;
               if ( !successSetValue )
               {
                  // Add debug message
               }
            }

            /* sml:setArrayValues */
            ossimString xpath_setArrayValues = "/gmlcov:sensorInstance/sml:SimpleProcess/sml:configuration/sml:Settings/sml:setArrayValues";
            xpath_setArrayValues = xpath_setArrayValues.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
            xpath = xpath0 + xpath_setArrayValues;
            xml_nodes.clear();
            m_xmlDocument->findNodes( xpath, xml_nodes );
            nXmlNodes = xml_nodes.size();
            for( size_t i=0; i<nXmlNodes; ++i )
            {
               ossimString elementValue( "" );

               const ossimRefPtr<ossimXmlAttribute> refAttribute = xml_nodes[i]->findAttribute( refStr );
               const ossimString& settingsRef = refAttribute->getValue();

               const ossimXmlNode::ChildListType& children = xml_nodes[i]->getChildNodes();
               if ( children.size() > 0 )
               {
                  const ossimXmlNode::ChildListType& grandchildren = children[0]->getChildNodes();
                   
                  if ( (grandchildren.size() > 1) && (grandchildren[1]->getTag() == ossimString( "sml:value")) )
                  {
                     elementValue = grandchildren[1]->getText();
                  }
               }

               bool successSetArrayValues = sensor_model->getImageGeometry( settingsRef, elementValue, geomKwl );
               success &= successSetArrayValues;
               if ( !successSetArrayValues )
               {
                  // Add debug message
               }
            }
         }
      }
      else
         if ( gotRectifiedImage )
         {
            const ossimString srsNameStr( "srsName" );
            ossimString pcsCodeDefinitionStr( "http://www.opengis.net/def/crs/EPSG/0/" );

            /* axis labels for rectified imagery */

            ossimString xpath_axisLabels = "/gml:axisLabels";
            xpath_axisLabels = xpath_axisLabels.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
            ossimString firstAxisLabelString( "" );
            ossimString secondAxisLabelString( "" );
            ossimString xpath = xpath0 + xpath_axisLabels;
            xml_nodes.clear();
            m_xmlDocument->findNodes( xpath, xml_nodes );
            if ( xml_nodes.size() == 1 )
            {
               const ossimString& axisLabelsString = xml_nodes[0]->getText();
               size_t spacePos = axisLabelsString.find( ' ' );
               firstAxisLabelString  = axisLabelsString.beforePos( spacePos );
               secondAxisLabelString = axisLabelsString.afterPos ( spacePos );
            }

            /* origin */

            ossim_uint32 pcsCodeOrigin = 32767;
            ossimString xpath_originPoint = "/gml:origin/gml:Point";
            xpath_originPoint = xpath_originPoint.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
            xpath = xpath0 + xpath_originPoint;
            xml_nodes.clear();
            m_xmlDocument->findNodes( xpath, xml_nodes );
            if ( xml_nodes.size() == 1 )
            {
               const ossimString& originString = xml_nodes[0]->getChildTextValue( ossimString( "pos" ) );
               size_t spacePos = originString.find( ' ' );
               ossimString firstOriginString  = originString.beforePos( spacePos );
               ossimString secondOriginString = originString.afterPos ( spacePos );

               const ossimRefPtr<ossimXmlAttribute> hrefAttribute = xml_nodes[0]->findAttribute( srsNameStr );
               const ossimString& originSrsName = hrefAttribute->getValue();
               ossimString pcsCodeOriginStr = originSrsName.after( pcsCodeDefinitionStr.string() );
               pcsCodeOrigin = pcsCodeOriginStr.toUInt32();
               if ( pcsCodeOrigin != 32767 )
               {
                  if ( pcsCodeOrigin != 4326 ) // map projection
                  {
                     /* Convert to geographic */
                  }

                  if ( firstAxisLabelString == ossimString( "Lat" ) )
                  {
                     geomKwl.add( ossimKeywordNames::ORIGIN_LATITUDE_KW, 
                                  firstOriginString.c_str() );
                     geomKwl.add( ossimKeywordNames::CENTRAL_MERIDIAN_KW, 
                                  secondOriginString.c_str() );
                  }
                  else
                  {
                     geomKwl.add( ossimKeywordNames::ORIGIN_LATITUDE_KW, 
                                  secondOriginString.c_str() );
                     geomKwl.add( ossimKeywordNames::CENTRAL_MERIDIAN_KW, 
                                  firstOriginString.c_str() );
                  }
               }
            }

            /* offset vector */

            ossimString xpath_offsetVector = "/gml:offsetVector";
            xpath_offsetVector = xpath_offsetVector.replaceAllThatMatch( defaultNamespaceStr.c_str(), "" );
            xpath = xpath0 + xpath_offsetVector;
            xml_nodes.clear();
            m_xmlDocument->findNodes( xpath, xml_nodes );
            size_t nNodes = xml_nodes.size();
            for ( size_t i=0; i<nNodes; ++i )
            {
               const ossimString& offsetVectorString = xml_nodes[i]->getText();
               size_t spacePos = offsetVectorString.find( ' ' );
               ossimString firstOffsetVectorString  = offsetVectorString.beforePos( spacePos );
               ossimString secondOffsetVectorString = offsetVectorString.afterPos ( spacePos );

               const ossimRefPtr<ossimXmlAttribute> hrefAttribute = xml_nodes[i]->findAttribute( srsNameStr );
               const ossimString& offsetVectorSrsName = hrefAttribute->getValue();
               ossimString pcsCodeOffsetVectorStr = offsetVectorSrsName.after( pcsCodeDefinitionStr.string() );
               ossim_uint32 pcsCodeOffsetVector = pcsCodeOffsetVectorStr.toUInt32();
               if ( pcsCodeOffsetVector == 4326 )
               {
                  if ( firstAxisLabelString == ossimString( "Lat" ) )
                  {
                     if ( firstOffsetVectorString.toDouble() != 0.0 )
                     {    
                        geomKwl.add( ossimKeywordNames::DECIMAL_DEGREES_PER_PIXEL_LAT, 
                                     firstOffsetVectorString.c_str() );
                     }
                     else
                     {
                        geomKwl.add( ossimKeywordNames::DECIMAL_DEGREES_PER_PIXEL_LON, 
                                     secondOffsetVectorString.c_str() );
                     }
                  }
                  else
                  {
                     if ( firstOffsetVectorString.toDouble() != 0.0 )
                     { 
                        geomKwl.add( ossimKeywordNames::DECIMAL_DEGREES_PER_PIXEL_LON, 
                                     firstOffsetVectorString.c_str() );
                     }
                     else
                     {
                        geomKwl.add( ossimKeywordNames::DECIMAL_DEGREES_PER_PIXEL_LAT, 
                                     secondOffsetVectorString.c_str() );
                     }
                  }
               }
               else // map projection
                  if ( pcsCodeOffsetVector == pcsCodeGrid )
                  {
                     if ( firstAxisLabelString == ossimString( "X" ) )
                     {
                        if ( firstOffsetVectorString.toDouble() != 0.0 )
                        {    
                           geomKwl.add( ossimKeywordNames::METERS_PER_PIXEL_X_KW, 
                                        firstOffsetVectorString.c_str() );
                        }
                        else
                        {
                           geomKwl.add( ossimKeywordNames::METERS_PER_PIXEL_Y_KW, 
                                        secondOffsetVectorString.c_str() );
                        }
                     }
                     else
                     {
                        if ( firstOffsetVectorString.toDouble() != 0.0 )
                        { 
                           geomKwl.add( ossimKeywordNames::METERS_PER_PIXEL_Y_KW, 
                                        firstOffsetVectorString.c_str() );
                        }
                        else
                        {
                           geomKwl.add( ossimKeywordNames::METERS_PER_PIXEL_X_KW, 
                                        secondOffsetVectorString.c_str() );
                        }
                     }
                  }
                  else
                  {
                     /*
                       Need to perform a coordinate conversion on the 
                       offset vector to the pcs code of the grid
                     */
                  }
            }
         }
   }

   return success;
   
} // End: ossimGmlSupportData::getImageGeometry( geoKwl )
Exemple #2
0
void Configuration::readApplicationSettings(xml_node<> *app)
{
  xml_node<> *sess = singleChildElement(app, "session-management");

  if (sess) {
    xml_node<> *dedicated = singleChildElement(sess, "dedicated-process");
    xml_node<> *shared = singleChildElement(sess, "shared-process");
    std::string tracking = singleChildElementValue(sess, "tracking", "");

    if (dedicated && shared)
      throw WServer::Exception("<application-settings> requires either "
			       "<dedicated-process> or <shared-process>, "
			       "not both");

    if (dedicated) {
      sessionPolicy_ = DedicatedProcess;
      setInt(dedicated, "max-num-sessions", maxNumSessions_);
    }

    if (shared) {
      sessionPolicy_ = SharedProcess;

      setInt(shared, "num-processes", numProcesses_);
    }

    if (!tracking.empty()) {
      if (tracking == "Auto")
	sessionTracking_ = CookiesURL;
      else if (tracking == "URL")
	sessionTracking_ = URL;
      else
	throw WServer::Exception("<session-tracking>: expecting 'Auto' "
				 "or 'URL'");
    }

    setInt(sess, "timeout", sessionTimeout_);
    setInt(sess, "bootstrap-timeout", bootstrapTimeout_);
    setInt(sess, "server-push-timeout", serverPushTimeout_);
    setBoolean(sess, "reload-is-new-session", reloadIsNewSession_);
  }

  std::string maxRequestStr
    = singleChildElementValue(app, "max-request-size", "");
  if (!maxRequestStr.empty())
    maxRequestSize_ = boost::lexical_cast< ::int64_t >(maxRequestStr) * 1024;

  std::string debugStr = singleChildElementValue(app, "debug", "");

  if (!debugStr.empty()) {
    if (debugStr == "stack")
      errorReporting_ = ErrorMessageWithStack;
    else if (debugStr == "true")
      errorReporting_ = NoErrors;
    else if (debugStr == "false")
      errorReporting_ = ErrorMessage;
    else
      throw WServer::Exception("<debug>: expecting 'true', 'false',"
			       "or 'stack'");
  }

  setInt(app, "num-threads", numThreads_);

  xml_node<> *fcgi = singleChildElement(app, "connector-fcgi");
  if (!fcgi)
    fcgi = app; // backward compatibility

  valgrindPath_ = singleChildElementValue(fcgi, "valgrind-path",
					  valgrindPath_);
  runDirectory_ = singleChildElementValue(fcgi, "run-directory",
					  runDirectory_);

  setInt(fcgi, "num-threads", numThreads_); // backward compatibility < 3.2.0

  xml_node<> *isapi = singleChildElement(app, "connector-isapi");
  if (!isapi)
    isapi = app; // backward compatibility

  setInt(isapi, "num-threads", numThreads_); // backward compatibility < 3.2.0

  std::string maxMemoryRequestSizeStr =
    singleChildElementValue(isapi, "max-memory-request-size", "");
  if (!maxMemoryRequestSizeStr.empty()) {
    isapiMaxMemoryRequestSize_ = boost::lexical_cast< ::int64_t >
      (maxMemoryRequestSizeStr) * 1024;
  }

  setInt(app, "session-id-length", sessionIdLength_);

  /*
   * If a session-id-prefix is defined in the configuration file, then
   * we loose the prefix defined by the connector (e.g. wthttpd), but who
   * would do such a thing ? */
  connectorSessionIdPrefix_
    = singleChildElementValue(app,"session-id-prefix",
			      connectorSessionIdPrefix_);

  setBoolean(app, "send-xhtml-mime-type", xhtmlMimeType_);
  if (xhtmlMimeType_)
    LOG_WARN("ignoring send-xhtml-mime-type setting: HTML5 is now always used");
  redirectMsg_ = singleChildElementValue(app, "redirect-message", redirectMsg_);

  setBoolean(app, "behind-reverse-proxy", behindReverseProxy_);
  setBoolean(app, "strict-event-serialization", serializedEvents_);
  setBoolean(app, "web-sockets", webSockets_);

  setBoolean(app, "inline-css", inlineCss_);
  setBoolean(app, "persistent-sessions", persistentSessions_);

  uaCompatible_ = singleChildElementValue(app, "UA-Compatible", "");

  setBoolean(app, "progressive-bootstrap", progressiveBoot_);
  if (progressiveBoot_)
    setBoolean(app, "split-script", splitScript_);
  setBoolean(app, "session-id-cookie", sessionIdCookie_);
  setBoolean(app, "cookie-checks", cookieChecks_);

  std::string plainAjaxSessionsRatioLimit
    = singleChildElementValue(app, "plain-ajax-sessions-ratio-limit", "");

  if (!plainAjaxSessionsRatioLimit.empty())
    maxPlainSessionsRatio_
      = boost::lexical_cast<float>(plainAjaxSessionsRatioLimit);

  setBoolean(app, "ajax-puzzle", ajaxPuzzle_);
  setInt(app, "indicator-timeout", indicatorTimeout_);
  setInt(app, "double-click-timeout", doubleClickTimeout_);

  std::vector<xml_node<> *> userAgents = childElements(app, "user-agents");

  for (unsigned i = 0; i < userAgents.size(); ++i) {
    xml_node<> *userAgentsList = userAgents[i];

    std::string type;
    if (!attributeValue(userAgentsList, "type", type))
      throw WServer::Exception("<user-agents> requires attribute 'type'");

    std::string mode;
    attributeValue(userAgentsList, "mode", mode);
    
    AgentList *list;
    if (type == "ajax") {
      list = &ajaxAgentList_;
      if (mode == "black-list")
	ajaxAgentWhiteList_ = false;
      else if (mode == "white-list")
	ajaxAgentWhiteList_ = true;
      else
	throw WServer::Exception
	  ("<user-agents type=\"ajax\" requires attribute 'mode' with value "
	   "\"white-list\" or \"black-list\"");
    } else if (type == "bot")
      list = &botList_;
    else
      throw WServer::Exception
	("<user-agents> requires attribute 'type' with value "
	 "\"ajax\" or \"bot\"");

    std::vector<xml_node<> *> agents
      = childElements(userAgentsList, "user-agent");

    for (unsigned j = 0; j < agents.size(); ++j)
      list->push_back(elementValue(agents[j], "user-agent"));
  }

  xml_node<> *properties = singleChildElement(app, "properties");

  if (properties) {
    std::vector<xml_node<> *> nodes = childElements(properties, "property");

    for (unsigned i = 0; i < nodes.size(); ++i) {
      xml_node<> *property = nodes[i];

      std::string name;
      if (!attributeValue(property, "name", name))
	throw WServer::Exception("<property> requires attribute 'name'");

      std::string value = elementValue(property, "property");

      if (name == "approot")
	name = "appRoot";

      if (name == "appRoot" && !appRoot_.empty())
	LOG_WARN("ignoring configuration property 'appRoot' ("
		 << value
		 << ") because was already set to " << appRoot_);
      else
        properties_[name] = value;
    }
  }
}