| /* |
| * 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, §ion, 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; |
| } |