Update of /cvsroot/playerstage/code/player/server/drivers/vectormap
In directory 
sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20455/server/drivers/vectormap

Added Files:
        .cvsignore Makefile.am dbconn.cc dbconn.h postgis.cc 
Log Message:
added vectormap interface
added postgis vectormap driver
Thanks to Ben Morelli for these changes


--- NEW FILE: .cvsignore ---
Makefile
Makefile.in


--- NEW FILE: dbconn.cc ---
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include "dbconn.h"
#ifdef HAVE_GEOS
#include <geos_c.h>
#endif

using namespace std;

bool PostgresConn::Connect(const char* dbname, const char* host, const char* 
user, const char* password, const char* port)
{
  conn = PQsetdbLogin(host, port, NULL, NULL, dbname, user, password);
  return (PQstatus(conn) != CONNECTION_BAD);
}

bool PostgresConn::Disconnect()
{
  PQfinish(conn);
  conn = NULL;
  //return (PQstatus(conn) == CONNECTION_BAD);
  return true;
}

VectorMapInfoHolder PostgresConn::GetVectorMapInfo(vector<string> layerNames)
{
  // Get the extent in the first query
  string query_string = "SELECT asbinary(extent(geom)) FROM (SELECT geom FROM ";

  for (uint ii=0; ii<layerNames.size(); ++ii)
  {
    if (ii == 0)
    {
      query_string += layerNames[ii];
    }
    else
    {
      query_string += " UNION SELECT geom FROM " + string(layerNames[ii]);
    }
  }
  query_string += ") AS layer_extent;";

  //PGresult* res = PQexec(conn, query_string.c_str());
  PGresult* res = PQexecParams(conn,
                               query_string.c_str(),
                               0,
                               NULL,
                               NULL,
                               NULL,
                               NULL,
                               1);

  if (PQresultStatus(res) != PGRES_TUPLES_OK)
  {
    cout << "Error performing select query on database!" << endl;
    cout << "No extent value found." << endl;
    cout << "GetVectorMapInfo() failed" << endl;
  }
 
  uint8_t* wkb_temp = reinterpret_cast<uint8_t*>(PQgetvalue(res, 0, 0));
  uint length = PQgetlength(res, 0, 0);
  uint8_t* wkb = new uint8_t[length];
  memcpy(wkb, wkb_temp, length);
  BoundingBox extent = BinaryToBBox(wkb, length);
  delete[] wkb;

  // Get the srid in the second query
  res = PQexec(conn, "SELECT srid FROM geometry_columns LIMIT 1;");
  if (PQresultStatus(res) != PGRES_TUPLES_OK)
  {
    cout << "Error performing select query on database!" << endl;
    cout << "No srid value found." << endl;
    cout << "GetVectorMapInfo() failed" << endl;
  }

  uint32_t srid = atoi(PQgetvalue(res, 0, 0));
  PQclear(res);

  VectorMapInfoHolder info(srid, extent);
  for (uint i=0; i<layerNames.size(); ++i)
  {
    LayerInfoHolder layer_info(layerNames[i]);
    LayerDataHolder layer_data(layer_info);
    info.layers.push_back(layer_data);
  }

  return info;
}

LayerInfoHolder PostgresConn::GetLayerInfo(const char* layer_name)
{
  LayerInfoHolder info;

  // Retrieve the extent of the layer in binary form
  const char* query_template = "SELECT asbinary(extent(geom)) AS extent FROM 
%s;";

  char query_string[MAX_PSQL_STRING];
  memset(query_string, 0, MAX_PSQL_STRING);
  snprintf(query_string, MAX_PSQL_STRING, query_template, layer_name);

  //PGresult* res = PQexec(conn, query_string);
  PGresult* res = PQexecParams(conn,
                               query_string,
                               0,
                               NULL,
                               NULL,
                               NULL,
                               NULL,
                               1);

  if (PQresultStatus(res) != PGRES_TUPLES_OK)
  {
    cout << "Error performing select query on database!" << endl;
    cout << "GetLayerInfo() failed" << endl;
  }

  info.name = layer_name;
  uint length = PQgetlength(res, 0, 0);
  uint8_t* wkb = new uint8_t[length];
  memcpy(wkb, PQgetvalue(res, 0, 0), length);

  info.extent = BinaryToBBox(wkb, length);

  PQclear(res);

  return info;
}

LayerDataHolder PostgresConn::GetLayerData(const char* layer_name)
{
  // Split into two queries. First get the layer meta-data, then get the 
feature data.
  // need to get layer name count, layer name, feature count and exteny
  LayerDataHolder data;

  data.info.name = layer_name;

  // need to get name count, name, wkb count, wkb
  const char* template_data = "SELECT name, asbinary(geom) FROM %s;";
  char query_data[MAX_PSQL_STRING];
  memset(query_data, 0, sizeof(MAX_PSQL_STRING));
  snprintf(query_data, MAX_PSQL_STRING, template_data, layer_name);

  //res = PQexec(conn, query_data);
  PGresult* res = PQexecParams(conn,
                         query_data,
                         0,
                         NULL,
                         NULL,
                         NULL,
                         NULL,
                         1);
  if (PQresultStatus(res) != PGRES_TUPLES_OK)
  {
    cout << "Error performing select query on database!" << endl;
    cout << "GetLayerData() data failed, returned NULL" << endl;
  }

  int num_rows = PQntuples(res);

  for (int i=0; i<num_rows; ++i)
  {
    FeatureDataHolder fd(string(PQgetvalue(res, i, 0)));
    uint8_t *wkb = reinterpret_cast<uint8_t *>(PQgetvalue(res, i, 1));
    uint32_t length = PQgetlength(res, i, 1);
    fd.wkb.assign(wkb, &wkb[length]);
    data.features.push_back(fd);
  }

  PQclear(res);

  return data;
}

BoundingBox PostgresConn::BinaryToBBox(const uint8_t* wkb, uint length)
{
  BoundingBox res;
  memset(&res, 0, sizeof(BoundingBox));
#ifdef HAVE_GEOS
  GEOSGeom polygon = NULL;
  polygon = GEOSGeomFromWKB_buf(wkb, length);
  if (polygon == NULL)
  {
    printf("GEOSGeomFromWKB_buf returned NULL!\n");
    return res;
  }
  GEOSGeom linestring = GEOSGetExteriorRing(polygon);
  if (linestring == NULL)
  {
    printf("GEOSGetExteriorRing returned NULL!\n");
    return res;
  }
  GEOSCoordSeq coords = GEOSGeom_getCoordSeq(linestring);
  if (coords == NULL)
  {
    printf("GEOSGeom_getCoordSeq returned NULL!\n");
    return res;
  }

  double xmin = INT_MAX, ymin = INT_MAX;
  double xmax = INT_MIN, ymax = INT_MIN;
  double tempX, tempY = 0;

  for (int ii=0; ii<GEOSGetNumCoordinates(linestring); ++ii)
  {
    GEOSCoordSeq_getX(coords, ii, &tempX);
    GEOSCoordSeq_getY(coords, ii, &tempY);
    if (tempX > xmax)
      xmax = tempX;
    if (tempX < xmin)
      xmin = tempX;
    if (tempY > ymax)
      ymax = tempY;
    if (tempY < ymin)
      ymin = tempY;
  }

  res.x0 = xmin;
  res.y0 = ymin;
  res.x1 = xmax;
  res.y1 = ymax;

#endif
  return res;
}

const player_vectormap_info_t* VectorMapInfoHolder::Convert()
{
  info.srid = srid;
  info.extent.x0 = extent.x0;
  info.extent.y0 = extent.y0;
  info.extent.x1 = extent.x1;
  info.extent.y1 = extent.y1;
  info.layers_count = layers.size();
  info.layers = new player_vectormap_layer_data_t[layers.size()];
  for (uint ii=0; ii<layers.size(); ++ii)
  {
    info.layers[ii] = *(layers[ii].Convert());
  }
  return &info;
}

VectorMapInfoHolder::~VectorMapInfoHolder()
{
//   if (info.layers)
//   {
//     delete[] info.layers;
//   }
}

const player_vectormap_layer_info_t* LayerInfoHolder::Convert()
{
  layer_info.name = strdup(name.c_str());
  layer_info.name_count = name.size() + 1;
  layer_info.extent.x0 = extent.x0;
  layer_info.extent.y0 = extent.y0;
  layer_info.extent.x1 = extent.x1;
  layer_info.extent.y1 = extent.y1;
  return &layer_info;
}

const player_vectormap_feature_data_t* FeatureDataHolder::Convert()
{
  feature_data.name = strdup(name.c_str());
  feature_data.name_count = name.size() + 1;
  feature_data.wkb = new uint8_t[wkb.size()];
  feature_data.wkb_count = wkb.size();
  ///TODO: Make more efficient
  for (uint ii=0; ii<wkb.size(); ++ii)
  {
    feature_data.wkb[ii] = wkb[ii];
  }
  return &feature_data;
}

FeatureDataHolder::~FeatureDataHolder()
{
//   if (feature_data.wkb)
//   {
//     delete[] feature_data.wkb;
//   }
}

const player_vectormap_layer_data_t* LayerDataHolder::Convert()
{
  layer_data.info = *(info.Convert());
  layer_data.features_count = features.size();
  layer_data.features = new player_vectormap_feature_data_t[features.size()];
  for (uint ii=0; ii<features.size(); ++ii)
  {
    layer_data.features[ii] = *(features[ii].Convert());
  }
  return &layer_data;
}

LayerDataHolder::~LayerDataHolder()
{
//   if (layer_data.features)
//   {
//     delete[] layer_data.features;
//   }
}

--- NEW FILE: dbconn.h ---
#ifndef __DBCONN_H_
#define __DBCONN_H_

#include <postgresql/libpq-fe.h>
#include <libplayercore/playercore.h>
#include <libplayercore/error.h>
#include <vector>

#define MAX_PSQL_STRING 256

using namespace std;

typedef struct
{
  double x0, y0, x1, y1;
}BoundingBox;

class FeatureDataHolder
{
  public:
    FeatureDataHolder(){};
    ~FeatureDataHolder();
    FeatureDataHolder(string name)
    { 
      this->name = name;
    };

    const player_vectormap_feature_data_t* Convert();

    string name;
    vector<uint8_t> wkb;
    player_vectormap_feature_data_t feature_data;
};

class LayerInfoHolder
{
  public:
    LayerInfoHolder(){};
    LayerInfoHolder(string name)
    {
      this->name = name;
      memset(&extent, 0, sizeof(extent));
    };

    const player_vectormap_layer_info_t* Convert();

    string name;
    BoundingBox extent;
    player_vectormap_layer_info_t layer_info;
};

class LayerDataHolder
{
  public:
    LayerDataHolder(){};
    ~LayerDataHolder();
    LayerDataHolder(LayerInfoHolder info)
    {
      this->info = info;
    };

    const player_vectormap_layer_data_t* Convert();

    LayerInfoHolder info;
    vector<FeatureDataHolder> features;
    player_vectormap_layer_data_t layer_data;
};

class VectorMapInfoHolder
{
  public:
    VectorMapInfoHolder(){};
    ~VectorMapInfoHolder();
    VectorMapInfoHolder(uint32_t srid, BoundingBox extent) { this->srid = srid; 
this->extent = extent; };

    const player_vectormap_info_t* Convert();

    uint32_t srid;
    vector<LayerDataHolder> layers;
    BoundingBox extent;
    player_vectormap_info_t info;
};

class PostgresConn
{
  public:
    PostgresConn(){ conn = NULL; };
    ~PostgresConn(){ if (Connected()) Disconnect(); };
    bool Connect(const char* dbname, const char* host, const char* user, const 
char* password, const char* port);
    bool Disconnect();
    bool Connected() { return (conn != NULL) && (PQstatus(conn) != 
CONNECTION_BAD); };
    const char* ErrorMessage() { return strdup(PQerrorMessage(conn)); };

    VectorMapInfoHolder GetVectorMapInfo(vector<string> layerNames);
    LayerInfoHolder GetLayerInfo(const char *layer_name);
    LayerDataHolder GetLayerData(const char *layer_name);

  private:
    BoundingBox BinaryToBBox(const uint8_t *binary, uint length);
    PGconn *conn;

};

#endif /* __DBCONN_H_ */

--- NEW FILE: Makefile.am ---
noinst_LTLIBRARIES =
if INCLUDE_POSTGIS
noinst_LTLIBRARIES += libpostgis.la
endif

AM_CPPFLAGS = -Wall -I$(top_srcdir) $(LIBPQXX_CFLAGS)

libpostgis_la_SOURCES = postgis.cc dbconn.cc dbconn.h
libpostgis_la_LIBADD = @GEOS_LIBS@
--- NEW FILE: postgis.cc ---
/*
 *  Player - One Hell of a Robot Server
 *  Copyright (C) 2004  Brian Gerkey [EMAIL PROTECTED]    
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/*
 * $Id: postgis.cc,v 1.1 2007/08/20 19:42:48 thjc Exp $
 *
 * A driver to use a vector map with a PostGIS backend.
 */

/** @ingroup drivers */
/** @{ */
/** @defgroup driver_PostGIS PostGIS
 * @brief Vectormap driver based on a Postgresql database with the PostGIS 
plugin.

@par Provides

- @ref interface_vectormap

@par Requires

- None

@par Configuration requests

- None

@par Configuration file options

- dbname (string)
  - Default: template1
  - The name of the Postgresql database to connect to.
- host (string)
  - Default: localhost
  - The name of the database host.
- user (string)
  - Default: postgres
  - The name of the database user.
- port (string)
  - Default: 5432
  - The port of the database.
- password (string)
  - Default: empty string
  - The password for the database user
- layers (string tuple)
  - Default: Field required
  - Names of the layers. The layers are named after the corresponding tables in 
the database.
@par Example 

@verbatim
driver
(
  dbname "gis"
  host "192.168.0.2"
  port "5433"
  user "postgres"
  password "secret"
  layers ["obstacles_geom" "markers_geom"]
)
@endverbatim

@par Creating a PostGIS Database
///TODO: Add documentation
For more information see http://postgis.refractions.net/

@par Database schema
///TODO: Add documentation

@author Ben Morelli

*/

/** @} */

#include <sys/types.h> // required by Darwin
#include <stdlib.h>
#include <libplayercore/playercore.h>
#include <libplayercore/error.h>
#include <playererror.h>
#include "dbconn.h"
#ifdef HAVE_GEOS
#include <geos_c.h>
#endif

/** Dummy function passed as a function pointer GEOS when it is initialised. 
GEOS uses this for logging. */
void geosprint(const char *text, ...)
{
  return;
}
////////////////////////////////////////////////////////////////////////////////
class PostGIS : public Driver
{
  public:
    PostGIS(ConfigFile* cf, int section,
            const char* dbname,
            const char* host,
            const char* user,
            const char* password,
            const char* port,
            vector<string> layerNames);
    ~PostGIS();
    int Setup();
    int Shutdown();

    // MessageHandler
    int ProcessMessage(MessageQueue * resp_queue,
                       player_msghdr * hdr,
                       void * data);

  private:
    void RequestLayerWrite(player_vectormap_layer_data_t* data);

    VectorMapInfoHolder RequestVectorMapInfo();
    LayerInfoHolder RequestLayerInfo(const char* layer_name);
    LayerDataHolder RequestLayerData(const char* layer_name);

    const char* dbname;
    const char* host;
    const char* user;
    const char* password;
    const char* port;
    vector<string> layerNames;

    PostgresConn *conn;
};

////////////////////////////////////////////////////////////////////////////////
Driver* PostGIS_Init(ConfigFile* cf, int section)
{
  const char* dbname = cf->ReadString(section,"dbname", "template1");
  const char* host = cf->ReadString(section,"host", "localhost");
  const char* user = cf->ReadString(section,"user", "postgres");
  const char* password = cf->ReadString(section,"password", "");
  const char* port = cf->ReadString(section,"port", "5432");

  vector<string> layerNames;

  int layers_count = cf->GetTupleCount(section, "layers");
  if (layers_count < 1)
  {
    PLAYER_ERROR("There must be at least one layer defined in the 'layers' 
configuration field.");
    return NULL;
  }

  for (int i=0; i<layers_count; ++i)
  {
    const char* layer_name = cf->ReadTupleString(section, "layers", i, "");
    layerNames.push_back(string(layer_name));
  }
  return((Driver*)(new PostGIS(cf, section, dbname, host, user, password, port, 
layerNames)));
}

////////////////////////////////////////////////////////////////////////////////
// a driver registration function
void PostGIS_Register(DriverTable* table)
{
  table->AddDriver("postgis", PostGIS_Init);
}

////////////////////////////////////////////////////////////////////////////////
PostGIS::PostGIS(ConfigFile* cf, int section,
                 const char* dbname,
                 const char* host,
                 const char* user,
                 const char* password,
                 const char* port,
                 vector<string> layerNames)
  : Driver(cf, section, true, PLAYER_MSGQUEUE_DEFAULT_MAXLEN, 
PLAYER_VECTORMAP_CODE)
{
  this->dbname = dbname;
  this->host = host;
  this->user = user;
  this->password = password;
  this->port = port;
  this->layerNames = layerNames;
  this->conn = NULL;

}

////////////////////////////////////////////////////////////////////////////////
PostGIS::~PostGIS()
{
  /*if (conn != NULL && conn->Connected())
  {
    conn->Disconnect();
  }
  if (conn != NULL)
  {
    printf("DELETING CONNECTION\n");
    delete conn;
    conn = NULL;
  }*/
}

////////////////////////////////////////////////////////////////////////////////
// Load resources
int PostGIS::Setup()
{
  PLAYER_MSG0(2, "PostGIS vectormap initialising");

#ifdef HAVE_GEOS
  PLAYER_MSG0(2, "Initialising GEOS");
  initGEOS(geosprint, geosprint);
  PLAYER_MSG0(2, "GEOS Initialised");
#endif

  conn = new PostgresConn();
  conn->Connect(dbname, host, user, password, port);

  if (!conn->Connected())
  {
    PLAYER_ERROR("Could not connect to Postgres database!");
    PLAYER_ERROR1("%s",conn->ErrorMessage());
    return(1);
  }

  PLAYER_MSG0(2, "PostGIS vectormap ready");
  return(0);
}

////////////////////////////////////////////////////////////////////////////////
// Clean up resources
int PostGIS::Shutdown()
{
  PLAYER_MSG0(2, "PostGIS vectormap shutting down");
  if (conn != NULL && conn->Connected())
  {
    PLAYER_MSG0(2, "Disconnecting database");
    conn->Disconnect();
  }
  if (conn != NULL)
  {
    delete conn;
    //conn == NULL;
  }
  
#ifdef HAVE_GEOS
  PLAYER_MSG0(2, "Shutting down GEOS");
  finishGEOS();
#endif
  PLAYER_MSG0(2, "PostGIS vectormap stopped");
  return(0);
}

////////////////////////////////////////////////////////////////////////////////
// Process an incoming message
int PostGIS::ProcessMessage(MessageQueue * resp_queue,
                            player_msghdr * hdr,
                            void * data)
{
  // Request for map info 
///////////////////////////////////////////////////////////
  if (Message::MatchMessage(hdr,  PLAYER_MSGTYPE_REQ,
                                  PLAYER_VECTORMAP_REQ_GET_MAP_INFO,
                                  this->device_addr))
  {

    if (hdr->size != 0)
    {
      PLAYER_ERROR2("request is wrong length (%d != %d); ignoring",
                 hdr->size, 0);
      return -1;
    }

    VectorMapInfoHolder info = RequestVectorMapInfo();
    const player_vectormap_info_t* response = info.Convert();

    this->Publish(this->device_addr,
                  resp_queue,
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_VECTORMAP_REQ_GET_MAP_INFO,
                  (void*)response,
                  sizeof(player_vectormap_info_t),
                  NULL);
    return(0);
  }
  // Request for layer info 
/////////////////////////////////////////////////////////
  else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
           PLAYER_VECTORMAP_REQ_GET_LAYER_INFO,
           this->device_addr))
  {

    if (hdr->size != sizeof(player_vectormap_layer_info_t))
    {
      PLAYER_ERROR2("request is wrong length (%d != %d); ignoring",
                    hdr->size, 0);
      return -1;
    }

    const player_vectormap_layer_info_t* request = 
reinterpret_cast<player_vectormap_layer_info_t*>(data);
    LayerInfoHolder info = RequestLayerInfo(request->name);
    const player_vectormap_layer_info_t* response = info.Convert();

    this->Publish(this->device_addr,
                  resp_queue,
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_VECTORMAP_REQ_GET_LAYER_INFO,
                  (void*)response,
                   sizeof(player_vectormap_layer_info_t),
                          NULL);

    return(0);
  }
  // Request for layer data 
/////////////////////////////////////////////////////////
  else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
                                      PLAYER_VECTORMAP_REQ_GET_LAYER_DATA,
                                      this->device_addr))
  {
    if (hdr->size != sizeof(player_vectormap_layer_data_t))
    {
      PLAYER_ERROR2("request is wrong length (%d != %d); ignoring",
                    hdr->size, 0);
      return -1;
    }

    player_vectormap_layer_data_t* request = 
reinterpret_cast<player_vectormap_layer_data_t*>(data);
    LayerDataHolder ldata = RequestLayerData(request->info.name);
    const player_vectormap_layer_data_t* response = ldata.Convert();

    this->Publish(this->device_addr,
                  resp_queue,
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_VECTORMAP_REQ_GET_LAYER_DATA,
                  (void*)response,
                  sizeof(player_vectormap_layer_data_t),
                  NULL);

    return(0);
  }
  // Request to write layer data 
///////////////////////////////////////////////////
  else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
                                      PLAYER_VECTORMAP_REQ_WRITE_LAYER,
                                      this->device_addr))
  {
    if (hdr->size != 0)
    {
      PLAYER_ERROR2("request is wrong length (%d != %d); ignoring",
                    hdr->size, 0);
      return -1;
    }
    player_vectormap_layer_data_t* request = 
reinterpret_cast<player_vectormap_layer_data_t*>(data);
    RequestLayerWrite(request);

    this->Publish(this->device_addr,
                  resp_queue,
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_VECTORMAP_REQ_WRITE_LAYER,
                  (void*)request,
                   sizeof(player_vectormap_layer_data_t),
                  NULL);
    return(0);
  }
  // Don't know how to handle this message 
/////////////////////////////////////////
  return(-1);
}

VectorMapInfoHolder PostGIS::RequestVectorMapInfo()
{
  if (conn == NULL || conn->Connected() == false)
  {
    PLAYER_ERROR("PostGis::RequestVectorMapInfo() failed! No db connection.");
  }

  VectorMapInfoHolder info = conn->GetVectorMapInfo(layerNames);

  return info;
}

LayerInfoHolder PostGIS::RequestLayerInfo(const char* layer_name)
{
  if (conn == NULL || conn->Connected() == false)
  {
    PLAYER_ERROR("PostGis::RequestLayerInfo() failed! No db connection.");
  }

  LayerInfoHolder info = conn->GetLayerInfo(layer_name);

  return info;
}

LayerDataHolder PostGIS::RequestLayerData(const char* layer_name)
{
  if (conn == NULL || conn->Connected() == false)
  {
    PLAYER_ERROR("PostGis::RequestLayerData() failed! No db connection.");
  }

  LayerDataHolder data = conn->GetLayerData(layer_name);

  return data;
}

void PostGIS::RequestLayerWrite(player_vectormap_layer_data_t* data)
{
  return;
}


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to