ConnectionCache.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004, The EROS Group, LLC and Johns Hopkins
00003  * University. All rights reserved.
00004  * 
00005  * This software was developed to support the EROS secure operating
00006  * system project (http://www.eros-os.org). The latest version of
00007  * the OpenCM software can be found at http://www.opencm.org.
00008  * 
00009  * Redistribution and use in source and binary forms, with or
00010  * without modification, are permitted provided that the following
00011  * conditions are met:
00012  * 
00013  * 1. Redistributions of source code must retain the above copyright
00014  *    notice, this list of conditions and the following disclaimer.
00015  * 
00016  * 2. Redistributions in binary form must reproduce the above
00017  *    copyright notice, this list of conditions and the following
00018  *    disclaimer in the documentation and/or other materials
00019  *    provided with the distribution.
00020  * 
00021  * 3. Neither the name of the The EROS Group, LLC nor the name of
00022  *    Johns Hopkins University, nor the names of its contributors
00023  *    may be used to endorse or promote products derived from this
00024  *    software without specific prior written permission.
00025  * 
00026  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
00027  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
00028  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00029  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00030  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
00031  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00032  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00033  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00034  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00035  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00036  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00037  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00038  * POSSIBILITY OF SUCH DAMAGE.
00039  */
00040 
00041 #include <repos/ConnectionCache.hxx>
00042 #include <util/avl.hxx>
00043 
00044 ConnectionCache ConnectionCache::theCache;
00045 
00046 #ifdef NEED_CONVERT
00047 /* A ConnectNode represents a connection to a repository. It maps a
00048    repos URI to both a "wrapped" repository and "unwrapped"
00049    repository.  Users connect to "wrapped" and "unwrapped"
00050    repositories in different circumstances.  For example, end users
00051    usually connect to an underlying (unwrapped) repository that's
00052    wrapped in an authrepos in order to support access checks to
00053    repository contents. Another example is that end users typically
00054    use a cacherepos in conjunction with a remote repository, so the
00055    (unwrapped) remote repository is wrapped in a cacherepos.  On the
00056    other hand, the client code must connect directly to an unwrapped
00057    cacherepos (using the anonymous user) in order to properly wrap a
00058    net repository for an end user.  Thus, the ConnectNode provides
00059    easy access to both the wrapped and unwrapped repositories for a
00060    given uri.  Note that for any given ConnectNode, the wrapped
00061    repository can be NULL, but the unwrapped repository will never be
00062    NULL. */
00063 
00064 /* Implementation of the repository connection cache. */
00065 #if 0
00066 typedef struct ConnectNode ConnectNode;
00067 struct ConnectNode {
00068   AvlNode avl;
00069   const char *uri;
00070   Repository *wrapped;
00071   Repository *unwrapped;
00072 };
00073 
00074 struct ConnectionCache {
00075   AvlTree *tree;
00076 };
00077 #endif
00078 
00079 /* Comparator function.  */
00080 static int
00081 cnode_uri_cmp(AvlNode *n1, AvlNode *n2)
00082 {
00083   ConnectNode *cn1 = (ConnectNode *)n1;
00084   ConnectNode *cn2 = (ConnectNode *)n2;
00085 
00086   /* Note that hashes don't buy us anything.  We really need the
00087   output of strcmp() in order to do the "greatest string less than or
00088   equal to" function */
00089   return strcmp(cn1->uri, cn2->uri);
00090 }
00091 
00092 ConnectionCache *
00093 ccache_create(void)
00094 {
00095   ConnectionCache *cache = GC_MALLOC(sizeof(ConnectionCache));
00096   cache->tree = avl_create(cnode_uri_cmp, gc_malloc);
00097 
00098   return cache;
00099 }
00100 
00101 static ConnectNode *
00102 ccache_lookup(ConnectionCache *c, URI *uri)
00103 {
00104   ConnectNode cn;
00105   ConnectNode *retNode;
00106   AvlNode *node = NULL;
00107 
00108   memset(&cn, 0, sizeof(cn));
00109   cn.uri = uri_toString(uri);
00110 
00111   /* This call returns the greatest key less than or equal to the
00112      search key */
00113   node = avl_lookupGLE(c->tree, (AvlNode *)(&cn));
00114   if (node == NULL)
00115     return NULL;
00116 
00117   /* Make sure the returned node's string is a prefix of the search
00118   string.  This filters out pseudo-matches like
00119   "file://joe@somewhere..." and "file://betty@elsewhere..." where the
00120   only really match is the uri scheme. */
00121   retNode = (ConnectNode *)node;
00122   if (path_isprefix(retNode->uri, cn.uri))
00123     return retNode;
00124 
00125   /* Else no match */
00126   return NULL;
00127 }
00128 
00129 static void 
00130 ccache_insert(ConnectionCache *c, URI *key, 
00131               Repository *wrapped, Repository *unwrapped)
00132 {
00133   ConnectNode *exists = ccache_lookup(c, key);
00134 
00135   if (exists)
00136     return;
00137 
00138   {
00139     ConnectNode *newNode = GC_NEW(ConnectNode);
00140 
00141     newNode->uri = uri_toString(key);
00142     newNode->wrapped = wrapped;
00143     newNode->unwrapped = unwrapped;
00144     avl_insert(c->tree, (AvlNode *)newNode);
00145   }
00146 }
00147 
00148 /* An iterator used to disconnect from the repositories in the
00149    connection cache. */
00150 static int
00151 ccache_disconnect(AvlNode *node)
00152 {
00153   ConnectNode *cd = (ConnectNode *)node;
00154 
00155   /* Calling disconnect on the wrapper should disconnect everything
00156   underneath.  If no "wrapped", then disconnect the "unwrapped"
00157   repository directly. Repository might already be disconnected, so
00158   catch exceptions. */
00159   TRY {
00160     if (cd->wrapped)
00161       repos_Disconnect(cd->wrapped);
00162     else
00163       repos_Disconnect(cd->unwrapped);
00164   }
00165   DEFAULT(AnyException) {
00166   }
00167   END_CATCH;
00168 
00169   return 0;
00170 }
00171 
00172 void
00173 ccache_destroy(ConnectionCache *c)
00174 {
00175   if (c) {
00176 
00177     /* Disconnect every repository in the cache */
00178     avl_iterate(c->tree, ccache_disconnect);
00179 
00180     /* Set var to NULL so it will be GC'ed */
00181     c = NULL;
00182   }
00183 }
00184 #endif /* NEED_CONVERT */
00185 
00186 GCPtr<Repository>
00187 ConnectionCache::doGetRepository(GCPtr<URI> uri)
00188 {
00189   assert(false);
00190 }
00191 
00192 GCPtr<Repository>
00193 ConnectionCache::doGetUnwrappedRepository(GCPtr<URI> uri)
00194 {
00195   assert(false);
00196 }
00197 
00198 #ifdef NEED_CONVERT
00199 /* Return a connected repository, either wrapped or unwrapped
00200 depending on what was resolved or found in the connection cache.  If
00201 there is a corresponding wrapped repository, return that in favor of
00202 the unwrapped.  In some cases, like the creation of an underlying
00203 cache repository, there won't be a wrapper repository.  In those
00204 cases, return the unwrapped, connected repository. */
00205 Repository *
00206 ccache_get_repository(ConnectionCache *c, URI *uri)
00207 {
00208   URI *key = NULL;
00209 
00210   if (!c)
00211     THROW(ExNullArg, format("NULL connection cache encountered."));
00212 
00213   /* See if there's already a connection for this uri */
00214   ConnectNode *cnode = ccache_lookup(c, uri);
00215   if (cnode == NULL) {
00216 
00217     /* Open and connect to specified repository */
00218     Repository *wrapped = NULL;
00219     Repository *unwrapped = repository_open(uri);
00220     repos_Connect(unwrapped);
00221 
00222     /* Wrapping logic: for all repositories that need it wrap
00223        appropriately with an authRepos, cacheRepos, or both. */
00224     if (REPOS_NEEDS_AUTHWRAP(unwrapped))
00225       wrapped = authrepository_wrap(unwrapped);
00226 
00227     if (REPOS_IS_REMOTE(unwrapped))
00228       wrapped = cacherepository_wrap(unwrapped);
00229 
00230     if (wrapped)
00231       key = wrapped->uri;
00232     else
00233       key = unwrapped->uri;
00234 
00235     ccache_insert(c, key, wrapped, unwrapped);
00236 
00237     return wrapped ? wrapped : unwrapped;
00238    } 
00239   else {
00240     if (cnode->wrapped == NULL)
00241       return cnode->unwrapped;
00242     else
00243       return cnode->wrapped;
00244   }
00245 
00246   THROW(ExNoObject, format("Couldn't get repository for \"%s\".", uri->URI));
00247 
00248   return NULL;
00249 }
00250 
00251 /* This call is the exception rather than the rule.  Use this when the
00252 caller specifically needs the unwrapped connection, even if there
00253 exists a wrapped connection.  Example:  "cm admin gc" wants to do the
00254 gc on the underlying (unwrapped) repository. */
00255 Repository *
00256 ccache_get_unwrapped_repository(ConnectionCache *c, URI *uri)
00257 {
00258   /* See if there's already a connection for this uri */
00259   ConnectNode *cnode = ccache_lookup(c, uri);
00260   if (cnode == NULL) {
00261 
00262     /* Open and connect to specified repository */
00263     Repository *unwrapped = repository_open(uri);
00264     repos_Connect(unwrapped);
00265 
00266     /* Skip the wrapping logic and insert this one without a wrapper */
00267     ccache_insert(c, unwrapped->uri, NULL, unwrapped);
00268     return unwrapped;
00269    } 
00270   else {
00271     return cnode->unwrapped;
00272   }
00273 
00274   THROW(ExNoObject, format("Couldn't get repository for \"%s\".", uri->URI));
00275 
00276   return NULL;
00277 }
00278 
00279 #endif /* NEED_CONVERT */

Generated on Sun Apr 23 22:42:35 2006 for OpenCM by  doxygen 1.4.6