2004-06-30 Helge Hess <helge.hess@opengroupware.org>
+ * OCSChannelManager.m: implemented pooling
+
* OCSFolder.m: added quick fetches
* GNUmakefile.preamble: fix link path
This object manages the connection pooling.
*/
-@class NSURL, NSMutableDictionary;
+@class NSURL, NSMutableDictionary, NSMutableArray, NSTimer;
@class EOAdaptorChannel, EOAdaptor;
@interface OCSChannelManager : NSObject
{
NSMutableDictionary *urlToAdaptor;
+
+ NSMutableArray *availableChannels;
+ NSMutableArray *busyChannels;
+ NSTimer *gcTimer;
}
+ (id)defaultChannelManager;
@interface OCSChannelHandle : NSObject
{
@public
+ NSURL *url;
EOAdaptorChannel *channel;
NSDate *creationTime;
NSDate *lastReleaseTime;
NSDate *lastAcquireTime;
}
+- (EOAdaptorChannel *)channel;
+- (BOOL)canHandleURL:(NSURL *)_url;
+- (NSTimeInterval)age;
+
@end
@implementation OCSChannelManager
-static BOOL debugOn = YES;
+static BOOL debugOn = YES;
+static BOOL debugPools = YES;
+static int ChannelExpireAge = 180;
+ (void)initialize {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
debugOn = [ud boolForKey:@"OCSChannelManagerDebugEnabled"];
+ ChannelExpireAge = [[ud objectForKey:@"OCSChannelExpireAge"] intValue];
+ if (ChannelExpireAge < 1)
+ ChannelExpireAge = 180;
}
+ (NSString *)adaptorNameForURLScheme:(NSString *)_scheme {
- (id)init {
if ((self = [super init])) {
- self->urlToAdaptor = [[NSMutableDictionary alloc] initWithCapacity:4];
+ self->urlToAdaptor = [[NSMutableDictionary alloc] initWithCapacity:4];
+ self->availableChannels = [[NSMutableArray alloc] initWithCapacity:16];
+ self->busyChannels = [[NSMutableArray alloc] initWithCapacity:16];
}
return self;
}
- (void)dealloc {
- [self->urlToAdaptor release];
+ if (self->gcTimer) [self->gcTimer invalidate];
+ [self->gcTimer release];
+
+ [self->busyChannels release];
+ [self->availableChannels release];
+ [self->urlToAdaptor release];
[super dealloc];
}
/* channels */
+- (OCSChannelHandle *)findBusyChannelHandleForChannel:(EOAdaptorChannel *)_ch {
+ NSEnumerator *e;
+ OCSChannelHandle *handle;
+
+ e = [self->busyChannels objectEnumerator];
+ while ((handle = [e nextObject])) {
+ if ([handle channel] == _ch)
+ return handle;
+ }
+ return nil;
+}
+- (OCSChannelHandle *)findAvailChannelHandleForURL:(NSURL *)_url {
+ NSEnumerator *e;
+ OCSChannelHandle *handle;
+
+ e = [self->availableChannels objectEnumerator];
+ while ((handle = [e nextObject])) {
+ if ([handle canHandleURL:_url])
+ return handle;
+
+ if (debugPools) {
+ [self logWithFormat:@"DBPOOL: cannot use handle (%@ vs %@)",
+ [_url absoluteString], [handle->url absoluteString]];
+ }
+ }
+ return nil;
+}
+
- (EOAdaptorChannel *)_createChannelForURL:(NSURL *)_url {
EOAdaptor *adaptor;
EOAdaptorContext *adContext;
- (EOAdaptorChannel *)acquireOpenChannelForURL:(NSURL *)_url {
// TODO: naive implementation, add pooling!
EOAdaptorChannel *channel;
+ OCSChannelHandle *handle;
+ NSCalendarDate *now;
+
+ now = [NSCalendarDate date];
+
+ /* look for cached handles */
+
+ if ((handle = [self findAvailChannelHandleForURL:_url]) != nil) {
+ // TODO: check age?
+ [self->busyChannels addObject:handle];
+ [self->availableChannels removeObject:handle];
+ ASSIGN(handle->lastAcquireTime, now);
+
+ if (debugPools)
+ [self logWithFormat:@"DBPOOL: reused cached DB channel!"];
+ return [[handle channel] retain];
+ }
+
+ if (debugPools) {
+ [self logWithFormat:@"DBPOOL: create new DB channel for URL: %@",
+ [_url absoluteString]];
+ }
+
+ /* create channel */
if ((channel = [self _createChannelForURL:_url]) == nil)
- return channel;
+ return nil;
if ([channel isOpen])
- return [channel retain];
-
- if (![channel openChannel]) {
+ ;
+ else if (![channel openChannel]) {
[self logWithFormat:@"could not open channel %@ for URL: %@",
channel, _url];
return nil;
}
+ /* create handle for channel */
+
+ handle = [[OCSChannelHandle alloc] init];
+ handle->url = [_url retain];
+ handle->channel = [channel retain];
+ handle->creationTime = [now retain];
+ handle->lastAcquireTime = [now retain];
+
+ [self->busyChannels addObject:handle];
+ [handle release];
+
return [channel retain];
}
- (void)releaseChannel:(EOAdaptorChannel *)_channel {
- // TODO: naive implementation, add pooling!
+ OCSChannelHandle *handle;
+
+ if ((handle = [self findBusyChannelHandleForChannel:_channel]) != nil) {
+ NSCalendarDate *now;
+
+ now = [NSCalendarDate date];
+
+ handle = [handle retain];
+ ASSIGN(handle->lastReleaseTime, now);
+
+ [self->busyChannels removeObject:handle];
+
+ if ([[handle channel] isOpen] && [handle age] < ChannelExpireAge) {
+ // TODO: consider age
+ [self->availableChannels addObject:handle];
+ if (debugPools) {
+ [self logWithFormat:
+ @"DBPOOL: keeping channel (age %ds, #%d): %@",
+ (int)[handle age], [self->availableChannels count],
+ [handle->url absoluteString]];
+ }
+ [_channel release];
+ [handle release];
+ return;
+ }
+
+ [self logWithFormat:
+ @"DBPOOL: freeing old channel (age %ds)", (int)[handle age]];
+
+ /* not reusing channel */
+ [handle release]; handle = nil;
+ }
+
if ([_channel isOpen])
[_channel closeChannel];
return self->channel;
}
+- (BOOL)canHandleURL:(NSURL *)_url {
+ if (_url == nil) {
+ [self logWithFormat:@"MISMATCH: no url .."];
+ return NO;
+ }
+ if (_url == self->url)
+ return YES;
+
+ if (![[self->url host] isEqual:[_url host]]) {
+ [self logWithFormat:@"MISMATCH: different host .."];
+ return NO;
+ }
+ if (![[self->url ocsDatabaseName] isEqualToString:[_url ocsDatabaseName]]) {
+ [self logWithFormat:@"MISMATCH: different db .."];
+ return NO;
+ }
+ if (![[self->url user] isEqual:[_url user]]) {
+ [self logWithFormat:@"MISMATCH: different user .."];
+ return NO;
+ }
+ if ([[self->url port] intValue] != [[_url port] intValue]) {
+ [self logWithFormat:@"MISMATCH: different port (%@ vs %@) ..",
+ [self->url port], [_url port]];
+ return NO;
+ }
+
+ return YES;
+}
+
+- (NSTimeInterval)age {
+ return [[NSCalendarDate calendarDate]
+ timeIntervalSinceDate:self->creationTime];
+}
+
+/* NSCopying */
+
+- (id)copyWithZone:(NSZone *)_zone {
+ return [self retain];
+}
+
/* description */
- (NSString *)description {
2004-06-30 Helge Hess <helge.hess@opengroupware.org>
+ * UIxCalWeekOverview.wox, UIxCalMonthOverview.wox: some minor tweaks
+
* UIxAppointmentView.wox: made the attendee emails clickable
* UIxCalView.m: removed -fetchGIDs, moved -fetchCoreInfos to client
>
<var:month-info>
<var:if condition="hasHolidayInfo">
- <var:string value="holidayInfo" const:class="monthoverview_holidayinfo" />
+ <var:string value="holidayInfo"
+ const:class="monthoverview_holidayinfo" />
</var:if>
<var:foreach list="allDayApts" item="appointment">
- <a var:href="appointmentViewURL"><var:string value="shortTextForApt" /></a>
+ <a var:href="appointmentViewURL"
+ ><var:string value="shortTextForApt" /></a>
</var:foreach>
</var:month-info>
- <var:month-label const:orientation="top"
- dayOfWeek="dayOfWeek"
- const:class="monthoverview_title"
- >
+ <var:month-label const:orientation="top"
+ dayOfWeek="dayOfWeek"
+ const:class="monthoverview_title"
+ >
<var:string value="localizedNameOfDayOfWeek" />
- </var:month-label>
- <var:month-label const:orientation="left"
- weekOfYear="weekOfYear"
- class="weekStyle"
- >
+ </var:month-label>
+ <var:month-label const:orientation="left"
+ weekOfYear="weekOfYear"
+ class="weekStyle"
+ >
<a href="weekoverview"
var:queryDictionary="currentWeekQueryParameters"
><var:string value="weekOfYear" /></a>
- </var:month-label>
+ </var:month-label>
- <var:month-title class="contentStyle">
- <span class="monthoverview_day">
- <a href="dayoverview"
- var:queryDictionary="currentDayQueryParameters"
- ><var:string value="currentDay.dayOfMonth" /></a>
- </span>
- <br />
- <span class="monthoverview_day_new">
- <a href="new"
- var:queryDictionary="currentDayQueryParameters"
- >[new]</a>
- </span>
- </var:month-title>
+ <var:month-title class="contentStyle">
+ <span class="monthoverview_day">
+ <a href="dayoverview"
+ var:queryDictionary="currentDayQueryParameters"
+ ><var:string value="currentDay.dayOfMonth" /></a>
+ </span>
+ <br />
+ <span class="monthoverview_day_new">
+ <a href="new" var:queryDictionary="currentDayQueryParameters"
+ >[new]</a>
+ </span>
+ </var:month-title>
- <var:month>
- <a var:href="appointmentViewURL"
- class="monthoverview_content_link"
- var:title="shortTextForApt"
- var:queryDictionary="currentDayQueryParameters"
- ><var:string value="shortTitleForApt" /></a>
+ <var:month>
+ <a var:href="appointmentViewURL"
+ class="monthoverview_content_link"
+ var:title="shortTextForApt"
+ var:queryDictionary="currentDayQueryParameters"
+ ><var:string value="shortTitleForApt" /></a>
</var:month>
</var:month-overview>
</var:component>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td colspan="2">
- <var:component className="UIxCalSelectTab" const:selection="week" currentDate="selectedDate">
-
+ <var:component className="UIxCalSelectTab" const:selection="week"
+ currentDate="selectedDate">
<table border="0" cellpadding="4" width="100%" cellspacing="2">
<tr>
<td width="1%" align="left" valign="middle" bgcolor="#e8e8e0">
<table border='0' cellpadding='0' cellspacing='0'>
<tr>
- <td><img rsrc:src="icon_apt_overview_inactive.gif" title="Overview" alt="Overview" border="0" valign="top" /></td>
- <td><a href="weekchartview"><img rsrc:src="icon_apt_chart.gif" title="Chart" alt="Chart" border="0" valign="top" /></a></td>
- <td><a href="weeklistview"><img rsrc:src="icon_apt_list.gif" title="List" alt="List" border="0" valign="top" /></a></td>
+ <td><img rsrc:src="icon_apt_overview_inactive.gif"
+ title="Overview" alt="Overview" border="0"
+ valign="top" /></td>
+ <td><a href="weekchartview"><img
+ rsrc:src="icon_apt_chart.gif" title="Chart"
+ alt="Chart" border="0" valign="top" /></a></td>
+ <td><a href="weeklistview"><img
+ rsrc:src="icon_apt_list.gif" title="List"
+ alt="List" border="0" valign="top" /></a></td>
<td>
- <a href="weekcolumnview"><img rsrc:src="icon_apt_column_view.gif" title="Columns" alt="Columns" border="0" valign="top" /></a>
+ <a href="weekcolumnview"><img
+ rsrc:src="icon_apt_column_view.gif" title="Columns"
+ alt="Columns" border="0" valign="top" /></a>
</td>
</tr>
</table>
</td>
<td align="right" bgcolor="#e8e8e0">
- <table border='0' cellpadding='0' cellspacing='1'>
- <tr>
- <td class="button_auto_env" nowrap="true" valign='middle' align='center'>
- <a class="button_auto" href="weekprintview" var:queryDictionary="queryParameters" target="SOGoPrintView">printview</a>
- </td>
- <td class="button_auto_env" nowrap="true" valign='middle' align='center'>
- <a class="button_auto" href="proposal" var:queryDictionary="queryParameters">proposal</a>
- </td>
- </tr>
- </table>
+ <table border='0' cellpadding='0' cellspacing='1'>
+ <tr>
+ <td class="button_auto_env" nowrap="true" valign='middle'
+ align='center'>
+ <a class="button_auto" href="weekprintview"
+ var:queryDictionary="queryParameters"
+ target="SOGoPrintView">printview</a>
+ </td>
+ <td class="button_auto_env" nowrap="true" valign='middle'
+ align='center'>
+ <a class="button_auto" href="proposal"
+ var:queryDictionary="queryParameters">proposal</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
const:titleStyle="weekoverview_title"
contentStyle="contentStyle"
>
- <var:week-title>
- <table cellpadding="0"
- width="100%"
- border="0"
- cellspacing="0"
- var:class="titleStyle"
- >
- <tr>
- <td align="left" valign="top">
- <a href="dayoverview"
- var:queryDictionary="currentDayQueryParameters"
- class="weekoverview_title_daylink"
- ><var:string value="currentDay.dayOfMonth" /></a>
- </td>
- <td align="center" valign="top" width="97%">
- <var:string value="currentDayName" /><br />
- [<a href="new"
- var:queryDictionary="currentDayQueryParameters"
- class="weekoverview_title_newlink"
- >new</a>]
- </td>
- </tr>
- </table>
- </var:week-title>
- <var:if condition="hasDayInfo">
- <var:week-info>
- <var:if condition="hasHolidayInfo">
- <var:string value="holidayInfo"
- const:class="weekoverview_holidayinfo" />
- </var:if>
- <var:foreach list="allDayApts" item="appointment">
- <a var:href="appointmentViewURL"
- var:queryDictionary="currentDayQueryParameters"
- ><var:string value="shortTextForApt" /></a>
- </var:foreach>
- </var:week-info>
- </var:if>
+ <var:week-title>
+ <table cellpadding="0" width="100%" border="0" cellspacing="0"
+ var:class="titleStyle">
+ <tr>
+ <td align="left" valign="top">
+ <a href="dayoverview"
+ var:queryDictionary="currentDayQueryParameters"
+ class="weekoverview_title_daylink"
+ ><var:string value="currentDay.dayOfMonth" /></a>
+ </td>
+ <td align="center" valign="top" width="97%">
+ <var:string value="currentDayName" /><br />
+ [<a href="new"
+ var:queryDictionary="currentDayQueryParameters"
+ class="weekoverview_title_newlink"
+ >new</a>]
+ </td>
+ </tr>
+ </table>
+ </var:week-title>
+ <var:if condition="hasDayInfo">
+ <var:week-info>
+ <var:if condition="hasHolidayInfo">
+ <var:string value="holidayInfo"
+ const:class="weekoverview_holidayinfo" />
+ </var:if>
+ <var:foreach list="allDayApts" item="appointment">
+ <a var:href="appointmentViewURL"
+ var:queryDictionary="currentDayQueryParameters"
+ ><var:string value="shortTextForApt" /></a>
+ </var:foreach>
+ </var:week-info>
+ </var:if>
<var:week>
- <a var:href="appointmentViewURL"><var:string value="appointment.title"/></a>
+ <a var:href="appointmentViewURL"
+ ><small><var:string value="shortTextForApt" /></small></a>
</var:week>
- </var:week-overview>
+ </var:week-overview>
</var:component>
</td>
</tr>