get the hooks into the backend lined up right

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3503 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Linas Vepstas 2001-01-20 05:37:46 +00:00
parent 3dd1feacde
commit 9d14f77759
4 changed files with 132 additions and 50 deletions

View File

@ -26,7 +26,7 @@
typedef enum {
ERR_BACKEND_NONE = 0,
ERR_BACKEND_NO_ERR = 0,
ERR_BACKEND_MISC,
/* fileio errors */
@ -38,6 +38,7 @@ typedef enum {
ERR_BFILEIO_ALLOC,
/* network errors */
ERR_NETIO_NO_CONNECTION,
ERR_NETIO_SHORT_READ,
ERR_NETIO_WRONG_CONTENT_TYPE,
ERR_NETIO_NOT_GNCXML
@ -47,6 +48,22 @@ typedef enum {
typedef struct _backend Backend;
/*
* The book_begin() routine gives the backend a second initialization
* opportunity. It is suggested that the backend check that
* the URL is syntactically correct, and that it is actually
* reachable. This is probably(?) a good time to initialize
* the actual network connection.
*
* The book_load() routine should return at least an account tree,
* and all currencies. It does not have to return any transactions
* whatsoever, as these are obtained at a later stage when a user
* opens a register, resulting in a query being sent to the backend.
*
* (Its OK to send over transactinos at this point, but one should
* be careful of the network load; also, its possible that whatever
* is sent is not what the user wanted anyway, which is why its
* better to wait for the query).
*
* The trans_commit_edit() routine takes two transaction arguments:
* the first is the proposed new transaction; the second is the
* 'original' transaction. The second argument is here for
@ -74,6 +91,20 @@ typedef struct _backend Backend;
* cache of the split data. This will allow the gnucash client to
* continue functioning even when disconnected from the server:
* this is because it will have its local cache of data to work from.
*
* The sync() routine synchronizes the engine contents to the backend.
* This is done by using version numbers (hack alert -- the engine
* does not currently contain version numbers).
* If the engine contents are newer than what's in the backend, the
* data is stored to the backend. If the engine contents are older,
* then the engine contents are updated.
*
* Note that this sync operation is only meant to apply to the
* current contents of the engine. This routine is not intended
* to be used to fetch account/transaction data from the backend.
* (It might pull new splits from the backend, if this is what is
* needed to update an existing transaction. It might pull new
* currencies (??))
*
* The last_err member indicates the last error that occured.
* It should probably be implemented as an array (actually,
@ -82,8 +113,9 @@ typedef struct _backend Backend;
struct _backend
{
AccountGroup * (*book_load) (GNCBook *, const char * book_id);
void (*book_end) (GNCBook *);
void (*book_begin) (GNCBook *, const char *book_id);
AccountGroup * (*book_load) (Backend *);
void (*book_end) (Backend *);
int (*account_begin_edit) (Backend *, Account *, int defer);
int (*account_commit_edit) (Backend *, Account *);
int (*trans_begin_edit) (Backend *, Transaction *);
@ -91,6 +123,7 @@ struct _backend
int (*trans_rollback_edit) (Backend *, Transaction *);
void (*run_query) (Backend *, Query *);
void (*sync) (Backend *, AccountGroup *);
GNCBackendError last_err;
};

View File

@ -25,9 +25,10 @@
* Rudimentary implmentation right now; good enough for a demo,
* but that's all.
*
* HACK ALRT -- this should be moved into its own sbdirectory
* HACK ALERT -- this should be moved into its own subdirectory
* Mostly so that the engine build doesn't require libghttp
* as a dependency.
* as a dependency. In general, backends should be dynamically
* loaded ...
*/
@ -152,33 +153,46 @@ setup_request (XMLBackend *be)
}
}
/* ==================================================================== */
/* Initialize the connection ... ?? */
/* validate the URL for syntactic correctness ?? */
/* maybe check to see if the server is reachable? */
static void
xmlbeBookBegin (GNCBook *book, const char *url)
{
XMLBackend *be;
if (!book) return;
ENTER ("url is %s", url);
be = (XMLBackend *) xaccGNCBookGetBackend (book);
/* hack alert -- we store this first url as the url for inital
* contact & * for sending queries to
* this should be made customizable, I suppose ???? */
be->query_url = g_strdup (url);
LEAVE(" ");
}
/* ==================================================================== */
/* Load a set of accounts and currencies from the indicated URL. */
static AccountGroup *
xmlbeBookLoad (GNCBook *book, const char *url)
xmlbeBookLoad (Backend *bend)
{
XMLBackend *be;
XMLBackend *be = (XMLBackend *) bend;
AccountGroup *grp;
ghttp_request *request;
const char *bufp;
int len;
if (!book) return NULL;
ENTER ("url is %s", url);
be = (XMLBackend *) xaccGNCBookGetBackend (book);
/* hack alert -- we store this first url as some bogus url
* for sending queries to
* this should be made customizable, I suppose ???? */
be->query_url = g_strdup (url);
if (!be) return NULL;
/* build up a request for the URL */
setup_request (be);
request = be->request;
ghttp_set_uri (request, (char *) url);
ghttp_set_uri (request, be->query_url);
ghttp_set_type (request, ghttp_type_get);
ghttp_prepare (request);
ghttp_process (request);
@ -188,7 +202,7 @@ xmlbeBookLoad (GNCBook *book, const char *url)
if (0 >= len) return NULL;
bufp = ghttp_get_body(request);
grp = gncxml_read_from_buf (bufp, len);
grp = gncxml_read_from_buf ((char *)bufp, len);
LEAVE(" ");
return grp;
@ -267,6 +281,7 @@ xmlendNew (void)
be = (XMLBackend *) malloc (sizeof (XMLBackend));
/* generic backend handlers */
be->be.book_begin = NULL;
be->be.book_load = xmlbeBookLoad;
be->be.book_end = xmlbeBookEnd;
@ -276,8 +291,9 @@ xmlendNew (void)
be->be.trans_commit_edit = NULL;
be->be.trans_rollback_edit = NULL;
be->be.run_query = xmlbeRunQuery;
be->be.sync = NULL;
be->be.last_err = ERR_BACKEND_NONE;
be->be.last_err = ERR_BACKEND_NO_ERR;
be->request = ghttp_request_new();
be->auth_cookie = NULL;

View File

@ -324,47 +324,56 @@ gnc_book_begin (GNCBook *book, const char * book_id, gboolean ignore_lock)
/* -------------------------------------------------- */
if ((!strncmp(book_id, "http://", 7)) ||
(!strncmp(book_id, "https://", 8)))
(!strncmp(book_id, "https://", 8)) ||
(!strncmp(book_id, "postgres://", 11)))
{
/* Store the sessionid URL */
book->book_id = g_strdup (book_id);
/* create the backend */
book->backend = xmlendNew();
/* not sure what else should happen here ... should we check to see
* if the URL is reachable ?? Should we login the user ??
/* by definition, URL's are fully resolved */
/* hack alert -- the only reason we need to set 'fullpath' is
* because the file save gui dialogues check for fully resolved
* paths
*/
return TRUE;
}
/* -------------------------------------------------- */
if (!strncmp(book_id, "postgres://", 11))
{
/* Store the sessionid URL */
/* we expect this to be in the format
* postgres://some.hostname.com/databasename.pql
* or maybe
* postgres://localhost/databasename.pql
* or one can specify the postgres socket port number explicitly:
* postgres://some.hostname.com:5432/databasename.pql
*/
book->book_id = g_strdup (book_id);
book->fullpath = g_strdup (book_id);
/* load different backend based on URL. We should probably
* dynamically load these based on some config file ... */
if ((!strncmp(book_id, "http://", 7)) ||
(!strncmp(book_id, "https://", 8)))
{
/* create the backend */
book->backend = xmlendNew();
} else
if (!strncmp(book_id, "postgres://", 11))
{
/* #define SQLHACK */
#ifdef SQLHACK
{
extern Backend * pgendNew (void);
book->backend = pgendNew ();
#else
g_free(book->fullpath);
book->fullpath = NULL;
book->errtype = ENOSYS;
return FALSE;
#endif
}
/* if there's a begin method, call that. */
if (book->backend->book_begin)
{
(book->backend->book_begin)(book, book->book_id);
/* hack alert we should check for errors here ... */
/* not sure what else should happen here ... should we check to see
* if the URL is reachable ?? Should we login the user ??
*/
}
return TRUE;
#else
book->errtype = ENOSYS;
return FALSE;
#endif
}
/* -------------------------------------------------- */
/* -------------------------------------------------- */
/* otherwise, lets just assume its a file. */
rc = gnc_book_begin_file (book, book_id, ignore_lock);
@ -437,7 +446,7 @@ gnc_book_load (GNCBook *book)
* The GUI will need to do a query for that.
*/
if (be && be->book_load) {
book->topgroup = (be->book_load) (book, book->book_id);
book->topgroup = (be->book_load) (be);
}
xaccGroupSetBackend (book->topgroup, be);
@ -480,8 +489,22 @@ gnc_book_save_may_clobber_data (GNCBook *book)
void
gnc_book_save (GNCBook *book)
{
Backend *be;
if (!book) return;
/* if there is a backend, and the backend is reachablele
* (i.e. we can communicate with it), then synchronize with
* the backend. If we cannot contact the backend (e.g.
* because we've gone offline, the network has crashed, etc.)
* then give the user the option to save to disk.
*/
be = book->backend;
if (be && be->sync && book->topgroup)
{
(be->sync)(be, book->topgroup);
if (ERR_BACKEND_NO_ERR == be->last_err) return;
}
/* if the fullpath doesn't exist, either the user failed to initialize,
* or the lockfile was never obtained. Either way, we can't write. */
book->errtype = 0;
@ -510,6 +533,12 @@ gnc_book_end (GNCBook *book)
{
if (!book) return;
/* close down the backend first */
if (book->backend && book->backend->book_end)
{
(book->backend->book_end)(book->backend);
}
book->errtype = 0;
book->last_file_err = ERR_FILEIO_NONE;
@ -542,6 +571,9 @@ gnc_book_destroy (GNCBook *book)
gnc_book_end (book);
/* destroy the backend */
if (book->backend) g_free(book->backend);
xaccFreeAccountGroup (book->topgroup);
book->topgroup = NULL;

View File

@ -67,8 +67,9 @@ void gnc_book_destroy (GNCBook *book);
/* The gnc_book_begin () method begins a new book. It takes as an argument
* the book id. The book id must be a string in the form of a URI/URL.
* In the current implementation, only one type of URI is supported, and
* that is the file URI: anything of the form
* In the current implementation, only the file URI type is fully
* supported, (with others in various stages of prototyping).
* A file URI is of the form
* "file:/home/somewhere/somedir/file.xac"
* The path part must be a valid path. The file-part must be a valid
* xacc/gnucash-format file. Paths may be relative or absolute. If the