blob: ea0085426f3ebebd01c7dfb73dcd4c6d762d25cd [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Description: File based configuration.
*
* @author: Gal Shachor <shachor@il.ibm.com>
* @author: Costin Manolache
*/
#ifdef AS400
#include "apr_xlate.h"
#endif
#include "jk_global.h"
#include "jk_map.h"
#include "jk_pool.h"
#include "jk_config.h"
#define LENGTH_OF_LINE (1024)
/*
*/
static int jk2_config_file_saveConfig(jk_env_t *env,
jk_config_t *cfg, char *workerFile)
{
FILE *fp;
int i, j;
if (workerFile == NULL)
workerFile = cfg->file;
if (workerFile == NULL)
return JK_ERR;
#ifdef AS400
fp = fopen(workerFile, "w, o_ccsid=0");
#else
fp = fopen(workerFile, "w");
#endif
if (fp == NULL)
return JK_ERR;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"config.save(): Saving %s\n", workerFile);
/* We'll save only the objects/properties that were set
via config, and to the original 'string'. That keeps the config
small ( withou redundant ) and close to the original. Comments
will be saved/loaded later.
*/
for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
char *name = env->_objects->nameAt(env, env->_objects, i);
jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
if (mbean == NULL || mbean->settings == NULL)
continue;
if (strcmp(name, mbean->name) != 0) {
/* It's an alias. */
continue;
}
fprintf(fp, "[%s]\n", mbean->name);
for (j = 0; j < mbean->settings->size(env, mbean->settings); j++) {
char *pname = mbean->settings->nameAt(env, mbean->settings, j);
/* Don't save redundant information */
if (strcmp(pname, "name") != 0) {
fprintf(fp, "%s=%s\n",
pname,
mbean->settings->valueAt(env, mbean->settings, j));
}
}
fprintf(fp, "\n");
}
fclose(fp);
return JK_OK;
}
/* ==================== */
/* Reading / parsing.
*/
static void jk2_trim_prp_comment(char *prp)
{
#ifdef AS400
char *comment;
/* lots of lines that translate a '#' realtime deleted */
comment = strchr(prp, *APR_NUMBERSIGN);
#else
char *comment = strchr(prp, '#');
#endif
if (comment) {
*comment = '\0';
}
}
static int jk2_trim(char *s)
{
int i;
for (i = strlen(s) - 1; (i >= 0) && isspace(s[i]); i--);
s[i + 1] = '\0';
for (i = 0; ('\0' != s[i]) && isspace(s[i]); i++);
if (i > 0) {
strcpy(s, &s[i]);
}
return strlen(s);
}
int jk2_config_file_parseProperty(jk_env_t *env, jk_map_t *m, char **section,
char *prp)
{
int rc = JK_ERR;
char *v;
jk_map_t *prefNode = NULL;
jk2_trim_prp_comment(prp);
if (jk2_trim(prp) == 0)
return JK_OK;
/* Support windows-style 'sections' - for cleaner config
*/
if ((prp[0] == '[')) {
v = strchr(prp, ']');
*v = '\0';
jk2_trim(v);
prp++;
*section = m->pool->pstrdup(env, m->pool, prp);
jk2_map_default_create(env, &prefNode, m->pool);
m->add(env, m, *section, prefNode);
return JK_OK;
}
v = strchr(prp, '=');
if (v == NULL)
return JK_OK;
*v = '\0';
v++;
if (strlen(v) == 0 || strlen(prp) == 0)
return JK_OK;
if (*section != NULL) {
prefNode = m->get(env, m, *section);
}
else {
prefNode = m;
}
if (prefNode == NULL)
return JK_ERR;
/* fprintf(stderr, "Adding [%s] %s=%s\n", cfg->section, prp, v ); */
prefNode->add(env, prefNode, m->pool->pstrdup(env, m->pool, prp),
m->pool->pstrdup(env, m->pool, v));
return JK_OK;
}
/** Read the config file
*/
int jk2_config_file_read(jk_env_t *env, jk_map_t *m, const char *file)
{
FILE *fp;
char buf[LENGTH_OF_LINE + 1];
char *prp;
char *section = NULL;
if (m == NULL || file == NULL)
return JK_ERR;
#ifdef AS400
fp = fopen(file, "r, o_ccsid=0");
#else
fp = fopen(file, "r");
#endif
if (fp == NULL)
return JK_ERR;
while (NULL != (prp = fgets(buf, LENGTH_OF_LINE, fp))) {
jk2_config_file_parseProperty(env, m, &section, prp);
}
fclose(fp);
return JK_OK;
}
static int jk2_config_file_readFile(jk_env_t *env,
jk_config_t *cfg,
int *didReload, int firstTime)
{
int rc;
struct stat statbuf;
if (didReload != NULL)
*didReload = JK_FALSE;
if (cfg->file == NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"config.update(): No config file\n");
return JK_ERR;
}
rc = stat(cfg->file, &statbuf);
if (rc == -1) {
/* Don't report it as an error - it's a common case */
env->l->jkLog(env, env->l, JK_LOG_INFO,
"config.update(): Can't find config file %s\n",
cfg->file);
return JK_OK;
}
if (!firstTime && statbuf.st_mtime < cfg->mtime) {
if (cfg->mbean->debug > 0)
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"config.update(): No reload needed %s %ld %ld\n",
cfg->file, cfg->mtime, statbuf.st_mtime);
return JK_OK;
}
if (cfg->cs == NULL) {
jk_bean_t *jkb =
env->createBean2(env, cfg->mbean->pool, "threadMutex", NULL);
if (jkb != NULL && jkb->object != NULL) {
cfg->cs = jkb->object;
jkb->init(env, jkb);
}
}
if (cfg->cs != NULL)
cfg->cs->lock(env, cfg->cs);
/* Check if another thread has updated the config */
rc = stat(cfg->file, &statbuf);
if (rc == -1) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"config.update(): Can't find config file %s",
cfg->file);
if (cfg->cs != NULL)
cfg->cs->unLock(env, cfg->cs);
return JK_ERR;
}
if (!firstTime && statbuf.st_mtime <= cfg->mtime) {
if (cfg->cs != NULL)
cfg->cs->unLock(env, cfg->cs);
return JK_OK;
}
env->l->jkLog(env, env->l, JK_LOG_INFO,
"cfg.update() Updating config %s %d %d\n",
cfg->file, cfg->mtime, statbuf.st_mtime);
/** The map will be lost - all objects must be saved !
*/
jk2_map_default_create(env, &cfg->cfgData, env->tmpPool);
rc = jk2_config_file_read(env, cfg->cfgData, cfg->file);
if (rc == JK_OK) {
env->l->jkLog(env, env->l, JK_LOG_INFO,
"config.setConfig(): Reading properties %s %d\n",
cfg->file, cfg->cfgData->size(env, cfg->cfgData));
}
else {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"config.setConfig(): Error reading properties %s\n",
cfg->file);
if (cfg->cs != NULL)
cfg->cs->unLock(env, cfg->cs);
return JK_ERR;
}
rc = jk2_config_processConfigData(env, cfg, firstTime);
if (didReload != NULL)
*didReload = JK_TRUE;
cfg->mtime = statbuf.st_mtime;
if (cfg->cs != NULL)
cfg->cs->unLock(env, cfg->cs);
return rc;
}
static int jk2_config_file_update(jk_env_t *env,
jk_config_t *cfg, int *didReload)
{
return jk2_config_file_readFile(env, cfg, didReload, JK_FALSE);
}
/** Set a property for this config object
*/
static int JK_METHOD jk2_config_file_setAttribute(struct jk_env *env,
struct jk_bean *mbean,
char *name, void *valueP)
{
jk_config_t *cfg = mbean->object;
char *value = valueP;
if (strcmp(name, "file") == 0) {
cfg->file = value;
return jk2_config_file_readFile(env, cfg, NULL, JK_TRUE);
}
else if (strcmp(name, "debugEnv") == 0) {
env->debug = atoi(value);
}
else if (strcmp(name, "save") == 0) {
return jk2_config_file_saveConfig(env, cfg, cfg->file);
}
else {
return JK_ERR;
}
return JK_OK;
}
/** Get a property for this config object
*/
static void *JK_METHOD jk2_config_file_getAttribute(struct jk_env *env,
struct jk_bean *mbean,
char *name)
{
jk_config_t *cfg = mbean->object;
if (strcmp(name, "file") == 0) {
return cfg->file;
}
else if (strcmp(name, "ver") == 0) {
return 0;
}
else {
return "";
}
}
static char *myGetAttInfo[] = { "ver", "file", NULL };
static char *mySetAttInfo[] = { "ver", "file", "save", NULL };
int JK_METHOD jk2_config_file_factory(jk_env_t *env, jk_pool_t *pool,
jk_bean_t *result,
const char *type, const char *name)
{
jk_config_t *_this;
_this = (jk_config_t *)pool->alloc(env, pool, sizeof(jk_config_t));
if (_this == NULL)
return JK_ERR;
_this->pool = pool;
result->getAttributeInfo = myGetAttInfo;
result->setAttributeInfo = mySetAttInfo;
_this->ver = 0;
_this->setPropertyString = jk2_config_setPropertyString;
_this->setProperty = jk2_config_setProperty;
_this->processNode = jk2_config_processNode;
_this->cs = NULL;
_this->update = jk2_config_file_update;
_this->save = jk2_config_file_saveConfig;
result->object = _this;
result->setAttribute = jk2_config_file_setAttribute;
result->getAttribute = jk2_config_file_getAttribute;
_this->mbean = result;
return JK_OK;
}