Author: ivol37 at gmail.com
Date: Thu Jan  6 17:47:21 2011
New Revision: 573

Log:
[AMDATU-243] Enhanced config template manager with pluggable callback handler. 
This was required since the new 'seeds' property is a multi-value field with 
different syntax in the cassandra.yaml. Furthermore, moved cluster related 
configruation to ConfigurationAdmin. 

Added:
   
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateCallbackHandler.java
Modified:
   
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/CassandraConfigurationService.java
   
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraConfigurationServiceImpl.java
   
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraDaemonServiceImpl.java
   
trunk/amdatu-cassandra/cassandra-application/src/main/resources/conf/cassandra.yaml
   
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.cassandra.application.cfg
   
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateManager.java
   
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/service/ConfigTemplateManagerImpl.java

Modified: 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/CassandraConfigurationService.java
==============================================================================
--- 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/CassandraConfigurationService.java
      (original)
+++ 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/CassandraConfigurationService.java
      Thu Jan  6 17:47:21 2011
@@ -41,4 +41,10 @@
      * Configuration key for the working directory to use for Cassandra
      */
     public static final String CONFIG_WORKDIR = "workdir";
+
+    /**
+     * Configuration key that stores a list of IP addresses that are part of 
this cluster. The IP addresses
+     * are stored comma separated in ConfigurationAdmin.
+     */
+    public static final String SEEDS = "seeds";
 }

Modified: 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraConfigurationServiceImpl.java
==============================================================================
--- 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraConfigurationServiceImpl.java
  (original)
+++ 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraConfigurationServiceImpl.java
  Thu Jan  6 17:47:21 2011
@@ -22,6 +22,7 @@
 import java.util.Dictionary;
 
 import org.amdatu.cassandra.application.CassandraConfigurationService;
+import org.amdatu.core.config.templates.ConfigTemplateCallbackHandler;
 import org.amdatu.core.config.templates.ConfigTemplateManager;
 import org.apache.log4j.PropertyConfigurator;
 import org.osgi.framework.Bundle;
@@ -39,6 +40,7 @@
     // Statics
     private static final String STORAGE_CONF_SOURCE = "conf/cassandra.yaml";
     private static final String LOG4J_CONF_SOURCE = "conf/log4j.properties";
+    private static final String EOL = System.getProperty("line.separator");
 
     // Reference to the logservice
     private volatile LogService m_logService;
@@ -64,7 +66,7 @@
         try {
             if (!storageConfigFile.exists()) {
                 // Only write this file if it does not yet exist
-                m_configTemplateManager.writeConfiguration(url, 
storageConfigFile);
+                m_configTemplateManager.writeConfiguration(url, 
storageConfigFile, new CassandraCallbackHandler());
             }
             // Cassandra uses this system property to find its storage 
location.
             System.setProperty("cassandra.config", 
storageConfigFile.toURI().toString());
@@ -102,4 +104,22 @@
             m_workDir = new File(workBaseDir, (String) 
dictionary.get(CONFIG_WORKDIR));
         }
     }
+
+    class CassandraCallbackHandler implements ConfigTemplateCallbackHandler {
+        public String getValue(String pid, String property, Object 
configValue) {
+            if (CassandraConfigurationService.PID.equals(pid) && 
CassandraConfigurationService.SEEDS.equals(property)) {
+                // We must convert the comma separated list of IP addresses to 
the cassandra yaml format
+                String[] seeds = configValue.toString().split(",");
+                String result = "";
+                for (String seed : seeds) {
+                    result += EOL + "- " + seed;
+                }
+                return result;
+            } else {
+                // Return the value as-is
+                return configValue.toString();
+            }
+        }
+
+    }
 }

Modified: 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraDaemonServiceImpl.java
==============================================================================
--- 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraDaemonServiceImpl.java
 (original)
+++ 
trunk/amdatu-cassandra/cassandra-application/src/main/java/org/amdatu/cassandra/application/service/CassandraDaemonServiceImpl.java
 Thu Jan  6 17:47:21 2011
@@ -82,10 +82,9 @@
         if (m_daemonHasShutdown) {
             throw new RuntimeException("CassandraDaemon has already been 
shutdown and cannot be restarted.");
         }
-
         m_daemon.activate();
         m_cassandraServer = new CassandraServer();
-        m_logService.log(LogService.LOG_INFO, "Cassandra Daemon started. 
State="+ m_cassandraServer.state());
+        m_logService.log(LogService.LOG_INFO, "Cassandra Daemon started.");
     }
 
     public void stop() {

Modified: 
trunk/amdatu-cassandra/cassandra-application/src/main/resources/conf/cassandra.yaml
==============================================================================
--- 
trunk/amdatu-cassandra/cassandra-application/src/main/resources/conf/cassandra.yaml
 (original)
+++ 
trunk/amdatu-cassandra/cassandra-application/src/main/resources/conf/cassandra.yaml
 Thu Jan  6 17:47:21 2011
@@ -1,13 +1,13 @@
-# Cassandra storage config YAML 
+# Cassandra storage config YAML
 
-#NOTE !!!!!!!! NOTE 
+#NOTE !!!!!!!! NOTE
 # See http://wiki.apache.org/cassandra/StorageConfiguration for
 # full explanations of configuration directives
-#NOTE !!!!!!!! NOTE 
+#NOTE !!!!!!!! NOTE
 
 # The name of the cluster. This is mainly used to prevent machines in
 # one logical cluster from joining another.
-cluster_name: 'Amdatu Cluster'
+cluster_name: ${org.amdatu.core.cassandra.application/clustername}
 
 # You should always specify InitialToken when setting up a production
 # cluster for the first time, and often when adding capacity later.
@@ -51,17 +51,17 @@
 # directories where Cassandra should store data on disk.
 data_file_directories:
     - ${org.amdatu.core.cassandra.application/datafiledir}
-    
+
 # commit log
 commitlog_directory: ${org.amdatu.core.cassandra.application/commitlogdir}
 
 # saved caches
 saved_caches_directory: ${org.amdatu.core.cassandra.application/savedcachesdir}
 
-# Size to allow commitlog to grow to before creating a new segment 
+# Size to allow commitlog to grow to before creating a new segment
 commitlog_rotation_threshold_in_mb: 128
 
-# commitlog_sync may be either "periodic" or "batch." 
+# commitlog_sync may be either "periodic" or "batch."
 # When in batch mode, Cassandra won't ack writes until the commit log
 # has been fsynced to disk.  It will wait up to
 # CommitLogSyncBatchWindowInMS milliseconds for other writes, before
@@ -73,12 +73,11 @@
 # milliseconds.
 commitlog_sync_period_in_ms: 10000
 
-# Addresses of hosts that are deemed contact points. 
+# Addresses of hosts that are deemed contact points.
 # Cassandra nodes use this list of hosts to find each other and learn
 # the topology of the ring.  You must change this if you are running
 # multiple nodes!
-seeds:
-    - 127.0.0.1
+seeds: ${org.amdatu.core.cassandra.application/seeds}
 
 # Access mode.  mmapped i/o is substantially faster, but only practical on
 # a 64bit machine (which notably does not include EC2 "small" instances)
@@ -105,7 +104,7 @@
 # By default this will be set to the amount of data directories defined.
 #memtable_flush_writers: 1
 
-# Buffer size to use when performing contiguous column slices. 
+# Buffer size to use when performing contiguous column slices.
 # Increase this to the size of the column slices you typically perform
 sliced_buffer_size_in_kb: 64
 
@@ -115,19 +114,19 @@
 # Address to bind to and tell other Cassandra nodes to connect to. You
 # _must_ change this if you want multiple nodes to be able to
 # communicate!
-# 
+#
 # Leaving it blank leaves it up to InetAddress.getLocalHost(). This
 # will always do the Right Thing *if* the node is properly configured
 # (hostname, name resolution, etc), and the Right Thing is to use the
 # address associated with the hostname (it might not be).
 #
 # Setting this to 0.0.0.0 is always wrong.
-listen_address: localhost
+listen_address: ${org.amdatu.core.cassandra.application/listen_address}
 
 # The address to bind the Thrift RPC service to -- clients connect
 # here. Unlike ListenAddress above, you *can* specify 0.0.0.0 here if
 # you want Thrift to listen on all interfaces.
-# 
+#
 # Leaving this blank has the same effect it does for ListenAddress,
 # (i.e. it will be based on the configured hostname of the node).
 rpc_address: localhost
@@ -178,7 +177,7 @@
 # will be logged specifying the row key.
 in_memory_compaction_limit_in_mb: 64
 
-# Time to wait for a reply from other nodes before failing the command 
+# Time to wait for a reply from other nodes before failing the command
 rpc_timeout_in_ms: 10000
 
 # phi value that must be reached for a host to be marked down.
@@ -208,7 +207,7 @@
 dynamic_snitch: true
 # controls how often to perform the more expensive part of host score
 # calculation
-dynamic_snitch_update_interval_in_ms: 100 
+dynamic_snitch_update_interval_in_ms: 100
 # controls how often to reset all host scores, allowing a bad host to
 # possibly recover
 dynamic_snitch_reset_interval_in_ms: 600000
@@ -236,7 +235,7 @@
 # NoScheduler - Has no options
 # RoundRobin
 #  - throttle_limit -- The throttle_limit is the number of in-flight
-#                      requests per client.  Requests beyond 
+#                      requests per client.  Requests beyond
 #                      that limit are queued up until
 #                      running requests can complete.
 #                      The value of 80 here is twice the number of

Modified: 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.cassandra.application.cfg
==============================================================================
--- 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.cassandra.application.cfg
        (original)
+++ 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.core.cassandra.application.cfg
        Thu Jan  6 17:47:21 2011
@@ -8,4 +8,30 @@
 datafiledir=work/cassandra/data
 
 # Directory in which the caches are saved
-savedcachesdir=work/cassandra/saved_caches
\ No newline at end of file
+savedcachesdir=work/cassandra/saved_caches
+
+
+
+###############################
+# Clustering related properties
+###############################
+
+# Name of the Cassandra cluster to join. If a seed is defined in the 'seeds' 
property
+# that defines a different clustername, a clustername mismatch error will be 
displayed
+# and that seed will not become part of this cluster.
+clustername='Amdatu Cluster'
+
+# Comma separated list of IP addresses of nodes in the cluster to connect to.
+# Note that does not need to be a full listing of all IP addresses of all nodes
+# in the cluster, it may be a subset of it. This property determines with what
+# other nodes in the cluster this node will synchronize its data.
+# When cassandra is executed in stand-alone mode, this value should be 
127.0.0.1
+#seeds=127.0.0.1
+seeds=172.16.10.109,172.16.10.203
+
+# The IP address of this node that other cassandra nodes will use to connect 
to.
+# This IP address must thus be accessible from the other nodes in the cluster 
(to
+# be precise; those that provided this node as one of its seeds).
+# When cassandra is executed in stand-alone mode, this value should be 
localhost
+#listen_address=localhost
+listen_address=172.16.11.108
\ No newline at end of file

Added: 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateCallbackHandler.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateCallbackHandler.java
        Thu Jan  6 17:47:21 2011
@@ -0,0 +1,28 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.core.config.templates;
+
+public interface ConfigTemplateCallbackHandler {
+    /**
+     * 
+     * @param pid The PID of the detected configuration entry
+     * @param property The propername of the detected configution entry
+     * @param configValue The value retrieved from ConfigurationAdmin
+     * @return The actual value to replace the placeholder with
+     */
+    String getValue(String pid, String property, Object configValue);
+}

Modified: 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateManager.java
==============================================================================
--- 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateManager.java
        (original)
+++ 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/ConfigTemplateManager.java
        Thu Jan  6 17:47:21 2011
@@ -24,10 +24,10 @@
 /**
  * The Config Template Manager facilitates automatic replacement of 
configuration entries in static plain
  * text files (such as text, XML or RDF files). The manager will replace 
occurrences of ${[configid]} whenever
- * there is a configuration entry in the ConfigAdminService available matching 
[configid]. Several getConfiguration 
+ * there is a configuration entry in the ConfigAdminService available matching 
[configid]. Several getConfiguration
  * methods are available to do this for URLs, Files or StringBuffers.
  * When your service needs to act upon when any of the configuration entries 
used in your configuration files
- * are changed, you need to register a ConfigTemplateListener. 
+ * are changed, you need to register a ConfigTemplateListener.
  * @author ivol
  */
 public interface ConfigTemplateManager {
@@ -35,9 +35,9 @@
      * The PID of the configuration of this bundle.
      */
     public final static String PID = 
ConfigTemplateManager.class.getPackage().getName();
-    
+
     /**
-     * Replaces all occurrences of configuration entries in the plain text 
file pointed to 
+     * Replaces all occurrences of configuration entries in the plain text 
file pointed to
      * by the given URL with the value of these entries from the 
ConfigAdminService and returns
      * a new URL with the replacements.
      * By default the converted configuration file is cached forever. To 
remove a URL from the cache,
@@ -47,9 +47,9 @@
      * @throws IOException when an exception occurred during building the new 
URLof the converted config file
      */
     URL getConfigurationAsURL(URL templateURL) throws IOException;
-    
+
     /**
-     * Replaces all occurrences of configuration entries in the plain text 
file pointed to 
+     * Replaces all occurrences of configuration entries in the plain text 
file pointed to
      * by the given URL with the value of these entries from the 
ConfigAdminService and returns
      * the result as StringBuffer with the replacements.
      * By default the converted configuration file is cached forever. To 
remove a URL from the cache,
@@ -59,9 +59,9 @@
      * @throws IOException when an exception occurred during building the new 
URLof the converted config file
      */
     StringBuffer getConfigurationAsString(URL templateURL) throws IOException;
-    
+
     /**
-     * Replaces all occurrences of configuration entries in the plain text 
file pointed to 
+     * Replaces all occurrences of configuration entries in the plain text 
file pointed to
      * by the given URL with the value of these entries from the 
ConfigAdminService and writes the
      * result to the target file
      * @param templateURL The URL to the plain text file to replace 
configuration entries in
@@ -70,9 +70,22 @@
      * @throws IOException when an exception occurred during writing the 
converted config file to disk
      */
     void writeConfiguration(URL templateURL, File targetFile) throws 
IOException;
-    
+
     /**
-     * Replaces all occurrences of configuration entries in the plain text 
file pointed to 
+     * Replaces all occurrences of configuration entries in the plain text 
file pointed to
+     * by the given URL with the value of these entries from the 
ConfigAdminService and writes the
+     * result to the target file
+     * @param templateURL The URL to the plain text file to replace 
configuration entries in
+     * @param targetFile The target file to write to
+     * @param handler A custom ConfigTemplateCallbackHandler which will be 
invoked when replacing configuration
+     * entries. The callback handler defines the value to replace the 
placeholder with
+     * @return true if replacing all configuration entries succeeded, false 
otherwise
+     * @throws IOException when an exception occurred during writing the 
converted config file to disk
+     */
+    void writeConfiguration(URL templateURL, File targetFile, 
ConfigTemplateCallbackHandler handler) throws IOException;
+
+    /**
+     * Replaces all occurrences of configuration entries in the plain text 
file pointed to
      * by the given URL with the value of these entries from the 
ConfigAdminService and writes the
      * result to the target file
      * @param templateURL The URL to the plain text file to replace 
configuration entries in
@@ -81,7 +94,7 @@
      * @throws IOException when an exception occurred during writing the 
converted config file to disk
      */
     void writeConfiguration(URL templateURL, BufferedWriter writer) throws 
IOException;
-    
+
     /**
      * Removes the URL from the cache.
      * @param templateURL The URL to remove from the cache.

Modified: 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/service/ConfigTemplateManagerImpl.java
==============================================================================
--- 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/service/ConfigTemplateManagerImpl.java
    (original)
+++ 
trunk/amdatu-core/config-templates/src/main/java/org/amdatu/core/config/templates/service/ConfigTemplateManagerImpl.java
    Thu Jan  6 17:47:21 2011
@@ -32,6 +32,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.amdatu.core.config.templates.ConfigTemplateCallbackHandler;
 import org.amdatu.core.config.templates.ConfigTemplateManager;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
@@ -45,7 +46,7 @@
  */
 public class ConfigTemplateManagerImpl implements ConfigTemplateManager, 
ManagedService {
     // Regular expression to match configuration entries. Syntax: 
${[pid].[configentry]}
-    private static final Pattern CONFIG_ENTRY_REGEX = 
Pattern.compile("\\$\\{([A-Za-z0-9\\.-])+/([A-Za-z0-9\\.-])+\\}");
+    private static final Pattern CONFIG_ENTRY_REGEX = 
Pattern.compile("\\$\\{([A-Za-z0-9\\.-_])+/([A-Za-z0-9\\.-_])+\\}");
 
     // Services injected by the Felix dependency manager
     private volatile LogService m_logService;
@@ -94,7 +95,7 @@
                 return new URL(cachedUrl);
             } catch (MalformedURLException e) {
                 m_logService.log(LogService.LOG_ERROR, "Could not create URL 
from cached URI'" + cachedUrl
-                        + "'. Refreshing cache", e);
+                    + "'. Refreshing cache", e);
                 m_urlCache.remove(templateURL.toString());
             }
         }
@@ -134,11 +135,15 @@
     }
 
     public void writeConfiguration(URL templateURL, File targetFile) throws 
IOException {
+        writeConfiguration(templateURL, targetFile, null);
+    }
+
+    public void writeConfiguration(URL templateURL, File targetFile, 
ConfigTemplateCallbackHandler handler) throws IOException {
         if (!targetFile.exists()) {
             if (targetFile.getParentFile() != null && 
!targetFile.getParentFile().exists()) {
                 if (!targetFile.getParentFile().mkdir()) {
                     throw new IOException("Could not create parent directory '"
-                            + targetFile.getParentFile().getAbsolutePath() + 
"'");
+                        + targetFile.getParentFile().getAbsolutePath() + "'");
                 }
             }
             if (!targetFile.createNewFile()) {
@@ -150,7 +155,7 @@
         BufferedWriter writer = null;
         try {
             writer = new BufferedWriter(new FileWriter(targetFile));
-            writeConfiguration(templateURL, writer);
+            writeConfiguration(templateURL, writer, handler);
         } finally {
             if (writer != null) {
                 writer.close();
@@ -159,6 +164,10 @@
     }
 
     public void writeConfiguration(URL templateURL, BufferedWriter writer) 
throws IOException {
+        writeConfiguration(templateURL, writer, null);
+    }
+
+    public void writeConfiguration(URL templateURL, BufferedWriter writer, 
ConfigTemplateCallbackHandler handler) throws IOException {
         // Now write the converted entries to the writer
         BufferedReader reader = null;
         try {
@@ -168,7 +177,7 @@
                 String inputLine;
                 String outputLine;
                 while ((inputLine = reader.readLine()) != null) {
-                    outputLine = replaceConfigEntries(inputLine);
+                    outputLine = replaceConfigEntries(inputLine, handler);
                     writer.write(outputLine + "\r\n");
                 }
             } finally {
@@ -192,7 +201,7 @@
             if (file.exists()) {
                 if (!file.delete()) {
                     m_logService.log(LogService.LOG_INFO, "Failed to remove 
file '" + file.getAbsolutePath()
-                            + "' from cache");
+                        + "' from cache");
                 }
             }
         }
@@ -212,7 +221,7 @@
      * @param line
      * @return
      */
-    private String replaceConfigEntries(String line) {
+    private String replaceConfigEntries(String line, 
ConfigTemplateCallbackHandler handler) {
         // For performance reasons, return immediately if the line does not 
contain ${
         if (line.indexOf("${") == -1) {
             return line;
@@ -231,12 +240,17 @@
                     Object entry = config.getProperties().get(configentry);
                     if (entry != null) {
                         String value = entry.toString();
-                        newLine = newLine.replace(key, value);
+                        if (handler != null) {
+                            value = handler.getValue(pid, configentry, entry);
+                            newLine = newLine.replace(key, value);
+                        } else {
+                            newLine = newLine.replace(key, value);
+                        }
                     }
                 }
             } catch (IOException e) {
                 m_logService.log(LogService.LOG_WARNING, "Configuration for 
pid '" + pid
-                        + "' does not exist, entry ignored", e);
+                    + "' does not exist, entry ignored", e);
             }
         }
         return newLine;
@@ -257,4 +271,8 @@
         }
     }
 
+
+
+
+
 }

Reply via email to