killbill-aplcache

Changes

pom.xml 2(+1 -1)

util/pom.xml 34(+34 -0)

Details

diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PluginInfoJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PluginInfoJson.java
index 0aa0ad5..50910cb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PluginInfoJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PluginInfoJson.java
@@ -20,7 +20,7 @@ package org.killbill.billing.jaxrs.json;
 import java.util.Set;
 
 import org.killbill.billing.osgi.api.PluginInfo;
-import org.killbill.billing.osgi.api.PluginInfo.PluginServiceInfo;
+import org.killbill.billing.osgi.api.PluginServiceInfo;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 72f7917..9ca2659 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.62</version>
+        <version>0.63-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.15.9-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
index 0ac81f7..ade5a6e 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
@@ -73,6 +73,7 @@ import org.killbill.billing.util.glue.ClockModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
+import org.killbill.billing.util.glue.InfoModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
 import org.killbill.billing.util.glue.KillbillApiAopModule;
 import org.killbill.billing.util.glue.NonEntityDaoModule;
@@ -137,6 +138,7 @@ public class KillbillServerModule extends KillbillPlatformModule {
 
     protected void installKillbillModules() {
         install(new AuditModule(configSource));
+        install(new InfoModule(configSource));
         install(new BeatrixModule(configSource));
         install(new CacheModule(configSource));
         install(new CallContextModule(configSource));
diff --git a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
index d48507f..7360228 100644
--- a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
+++ b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
@@ -56,6 +56,7 @@ import org.killbill.billing.util.glue.CallContextModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
+import org.killbill.billing.util.glue.InfoModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
 import org.killbill.billing.util.glue.KillbillApiAopModule;
 import org.killbill.billing.util.glue.NonEntityDaoModule;
@@ -72,6 +73,7 @@ public class KillpayServerModule extends KillbillServerModule {
     @Override
     protected void installKillbillModules() {
         install(new AuditModule(configSource));
+        install(new InfoModule(configSource));
         install(new BeatrixModule(configSource));
         install(new CacheModule(configSource));
         install(new CallContextModule(configSource));

util/pom.xml 34(+34 -0)

diff --git a/util/pom.xml b/util/pom.xml
index 180d50a..94b4174 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -246,4 +246,38 @@
             <artifactId>jmxutils</artifactId>
         </dependency>
     </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.build.directory}/generated-sources/java-templates</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>templating-maven-plugin</artifactId>
+                <version>1.0-alpha-3</version>
+                <executions>
+                    <execution>
+                        <id>generate-verion-class</id>
+                        <goals>
+                            <goal>filter-sources</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/util/src/main/java/org/killbill/billing/util/glue/InfoModule.java b/util/src/main/java/org/killbill/billing/util/glue/InfoModule.java
new file mode 100644
index 0000000..f826d6d
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/InfoModule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.glue;
+
+import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.info.DefaultKillbillInfoApi;
+import org.killbill.billing.util.info.DefaultKillbillInfoService;
+import org.killbill.billing.util.info.KillbillInfoApi;
+import org.killbill.billing.util.info.KillbillInfoService;
+import org.killbill.billing.util.info.dao.DefaultNodeInfoDao;
+import org.killbill.billing.util.info.dao.NodeInfoDao;
+
+public class InfoModule extends KillBillModule {
+
+    public InfoModule(final KillbillConfigSource configSource) {
+        super(configSource);
+    }
+
+    protected void installDaos() {
+        bind(NodeInfoDao.class).to(DefaultNodeInfoDao.class).asEagerSingleton();
+    }
+
+    protected void installUserApi() {
+        bind(KillbillInfoApi.class).to(DefaultKillbillInfoApi.class).asEagerSingleton();
+        bind(KillbillInfoService.class).to(DefaultKillbillInfoService.class).asEagerSingleton();
+    }
+
+
+    @Override
+    protected void configure() {
+        installDaos();
+        installUserApi();
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/dao/DefaultNodeInfoDao.java b/util/src/main/java/org/killbill/billing/util/info/dao/DefaultNodeInfoDao.java
new file mode 100644
index 0000000..9226831
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/dao/DefaultNodeInfoDao.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info.dao;
+
+import java.util.Date;
+import java.util.List;
+
+import org.killbill.clock.Clock;
+import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.google.inject.Inject;
+
+public class DefaultNodeInfoDao implements NodeInfoDao {
+
+    private final IDBI dbi;
+    private final Clock clock;
+
+    @Inject
+    public DefaultNodeInfoDao(final IDBI dbi, final Clock clock) {
+        this.dbi = dbi;
+        this.clock = clock;
+        ((DBI) dbi).registerMapper(new LowerToCamelBeanMapperFactory(NodeInfoModelDao.class));
+
+    }
+
+    @Override
+    public void create(final NodeInfoModelDao nodeInfoModelDao) {
+
+        dbi.inTransaction(new TransactionCallback<Void>() {
+            @Override
+            public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
+                if (sqlDao.getByNodeName(nodeInfoModelDao.getNodeName()) != null) {
+                    sqlDao.delete(nodeInfoModelDao.getNodeName());
+                }
+                sqlDao.create(nodeInfoModelDao);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void updateNodeInfo(final String nodeName, final String nodeInfo) {
+        dbi.inTransaction(new TransactionCallback<Void>() {
+            @Override
+            public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
+                final Date updateDate = clock.getUTCNow().toDate();
+                sqlDao.updateNodeInfo(nodeName, nodeInfo, updateDate);
+                return null;
+            }
+        });
+    }
+
+
+    @Override
+    public void delete(final String nodeName) {
+        dbi.inTransaction(new TransactionCallback<Void>() {
+            @Override
+            public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
+                sqlDao.delete(nodeName);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public List<NodeInfoModelDao> getAll() {
+        return dbi.inTransaction(new TransactionCallback<List<NodeInfoModelDao>>() {
+            @Override
+            public List<NodeInfoModelDao> inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
+                return sqlDao.getAll();
+            }
+        });
+    }
+
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoDao.java b/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoDao.java
new file mode 100644
index 0000000..0d31498
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoDao.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info.dao;
+
+import java.util.List;
+
+public interface NodeInfoDao {
+
+    public void create(final NodeInfoModelDao nodeInfoModelDao);
+
+    public void updateNodeInfo(final String nodeName, final String nodeInfo);
+
+    public void delete(final String nodeName);
+
+    public List<NodeInfoModelDao> getAll();
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoModelDao.java b/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoModelDao.java
new file mode 100644
index 0000000..3230be8
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoModelDao.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info.dao;
+
+import org.joda.time.DateTime;
+
+public class NodeInfoModelDao {
+
+    private Long recordId;
+    private String nodeName;
+    private DateTime bootDate;
+    private DateTime updatedDate;
+    private String nodeInfo;
+    private Boolean isActive;
+
+    public NodeInfoModelDao() {
+    }
+
+    public NodeInfoModelDao(final Long recordId,
+                            final String nodeName,
+                            final DateTime bootDate,
+                            final DateTime updatedDate,
+                            final String nodeInfo,
+                            final Boolean isActive) {
+        this.recordId = recordId;
+        this.nodeName = nodeName;
+        this.bootDate = bootDate;
+        this.updatedDate = updatedDate;
+        this.nodeInfo = nodeInfo;
+        this.isActive = isActive;
+    }
+
+    public NodeInfoModelDao(final String nodeName,
+                            final DateTime bootDate,
+                            final String nodeInfo) {
+        this(-1L, nodeName, bootDate, bootDate, nodeInfo, true);
+    }
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public void setRecordId(final Long recordId) {
+        this.recordId = recordId;
+    }
+
+    public String getNodeName() {
+        return nodeName;
+    }
+
+    public void setNodeName(final String nodeName) {
+        this.nodeName = nodeName;
+    }
+
+    public DateTime getBootDate() {
+        return bootDate;
+    }
+
+    public void setBootDate(final DateTime bootDate) {
+        this.bootDate = bootDate;
+    }
+
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    public void setUpdatedDate(final DateTime updatedDate) {
+        this.updatedDate = updatedDate;
+    }
+
+    public String getNodeInfo() {
+        return nodeInfo;
+    }
+
+    public void setNodeInfo(final String nodeInfo) {
+        this.nodeInfo = nodeInfo;
+    }
+
+    public void setIsActive(final Boolean isActive) {
+        this.isActive = isActive;
+    }
+
+    // TODO  Required for making the BindBeanFactory with Introspector work
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
+    public Boolean isActive() {
+        return isActive;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof NodeInfoModelDao)) {
+            return false;
+        }
+
+        final NodeInfoModelDao that = (NodeInfoModelDao) o;
+
+        /*
+        if (recordId != null ? !recordId.equals(that.recordId) : that.recordId != null) {
+            return false;
+        }
+        */
+        if (nodeName != null ? !nodeName.equals(that.nodeName) : that.nodeName != null) {
+            return false;
+        }
+        if (bootDate != null ? bootDate.compareTo(that.bootDate) != 0 : that.bootDate != null) {
+            return false;
+        }
+        /*
+        if (updatedDate != null ? updatedDate.compareTo(that.updatedDate) != 0 : that.updatedDate != null) {
+            return false;
+        }
+        */
+        if (nodeInfo != null ? !nodeInfo.equals(that.nodeInfo) : that.nodeInfo != null) {
+            return false;
+        }
+        return !(isActive != null ? !isActive.equals(that.isActive) : that.isActive != null);
+
+    }
+
+    @Override
+    public int hashCode() {
+        /* int result = recordId != null ? recordId.hashCode() : 0; */
+        int result = nodeName != null ? nodeName.hashCode() : 0;
+        result = 31 * result + (bootDate != null ? bootDate.hashCode() : 0);
+        //result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
+        result = 31 * result + (nodeInfo != null ? nodeInfo.hashCode() : 0);
+        result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoSqlDao.java b/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoSqlDao.java
new file mode 100644
index 0000000..8a8edb2
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/dao/NodeInfoSqlDao.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info.dao;
+
+import java.util.Date;
+import java.util.List;
+
+import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+
+@EntitySqlDaoStringTemplate
+public interface NodeInfoSqlDao {
+
+    @SqlUpdate
+    public void create(@BindBean final NodeInfoModelDao nodeInfo);
+
+    @SqlUpdate
+    public void updateNodeInfo(@Bind("nodeName") final String nodeName, @Bind("nodeInfo") final String nodeInfo, @Bind("updatedDate") final Date updatedDate);
+
+    @SqlUpdate
+    public void delete(@Bind("nodeName") final String nodeName);
+
+    @SqlQuery
+    public NodeInfoModelDao getByNodeName(@Bind("nodeName") final String nodeName);
+
+    @SqlQuery
+    public List<NodeInfoModelDao> getAll();
+
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/DefaultKillbillInfoApi.java b/util/src/main/java/org/killbill/billing/util/info/DefaultKillbillInfoApi.java
new file mode 100644
index 0000000..6b9de08
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/DefaultKillbillInfoApi.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info;
+
+import org.killbill.billing.osgi.api.PluginInfo;
+
+public class DefaultKillbillInfoApi implements KillbillInfoApi {
+
+    @Override
+    public Iterable<NodeInfo> getNodeInfo() {
+        return null;
+    }
+
+    @Override
+    public void updatePluginInfo(final Iterable<PluginInfo> iterable) {
+
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/DefaultKillbillInfoService.java b/util/src/main/java/org/killbill/billing/util/info/DefaultKillbillInfoService.java
new file mode 100644
index 0000000..2f312de
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/DefaultKillbillInfoService.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.killbill.CreatorName;
+import org.killbill.billing.osgi.api.PluginInfo;
+import org.killbill.billing.osgi.api.PluginsInfoApi;
+import org.killbill.billing.platform.api.LifecycleHandlerType;
+import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
+import org.killbill.billing.util.info.dao.NodeInfoDao;
+import org.killbill.billing.util.info.dao.NodeInfoModelDao;
+import org.killbill.clock.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.joda.JodaModule;
+import com.google.common.collect.ImmutableList;
+
+public class DefaultKillbillInfoService implements KillbillInfoService {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultKillbillInfoService.class);
+
+    public static final String INFO_SERVICE_NAME = "info-service";
+
+    private final ObjectMapper mapper;
+
+    private final NodeInfoDao nodeInfoDao;
+    private final PluginsInfoApi pluginInfoApi;
+    private final Clock clock;
+
+    @Inject
+    public DefaultKillbillInfoService(final NodeInfoDao nodeInfoDao, final PluginsInfoApi pluginInfoApi, final Clock clock) {
+        this.nodeInfoDao = nodeInfoDao;
+        this.pluginInfoApi = pluginInfoApi;
+        this.clock = clock;
+        this.mapper = new ObjectMapper();
+        mapper.registerModule(new JodaModule());
+        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+
+    }
+
+    @Override
+    public String getName() {
+        return INFO_SERVICE_NAME;
+    }
+
+    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.START_SERVICE)
+    public void start() {
+        try {
+            createBootNodeInfo();
+        } catch (JsonProcessingException e) {
+            logger.error("Failed to create bootNodeInfo", e);
+        }
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
+    public void stop() {
+        nodeInfoDao.delete(CreatorName.get());
+    }
+
+    private void createBootNodeInfo() throws JsonProcessingException {
+
+        final DateTime bootTime = clock.getUTCNow();
+        final Iterable<PluginInfo> rawPluginInfo = pluginInfoApi.getPluginsInfo();
+        final List<PluginInfo> pluginInfo = rawPluginInfo.iterator().hasNext() ? ImmutableList.<PluginInfo>copyOf(rawPluginInfo) : ImmutableList.<PluginInfo>of();
+        final String kbVersion = org.killbill.billing.util.info.KillbillVersions.getKillbillVersion();
+        final String kbApiVersion  = org.killbill.billing.util.info.KillbillVersions.getApiVersion();
+        final String kbPluginApiVersion  = org.killbill.billing.util.info.KillbillVersions.getPluginApiVersion();
+        final String kbPlatformVersion  = org.killbill.billing.util.info.KillbillVersions.getPlatformVersion();
+        final String kbCommonVersion  = org.killbill.billing.util.info.KillbillVersions.getCommonVersion();
+        final NodeInfo nodeInfo = new DefaultNodeInfo(CreatorName.get(), bootTime, bootTime, kbVersion, kbApiVersion, kbPluginApiVersion, kbCommonVersion, kbPlatformVersion, pluginInfo);
+        final String nodeInfoValue = mapper.writeValueAsString(nodeInfo);
+        final NodeInfoModelDao bootNodeInfo = new NodeInfoModelDao(CreatorName.get(), clock.getUTCNow(), nodeInfoValue);
+        nodeInfoDao.create(bootNodeInfo);
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/DefaultNodeInfo.java b/util/src/main/java/org/killbill/billing/util/info/DefaultNodeInfo.java
new file mode 100644
index 0000000..3b6c3cf
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/DefaultNodeInfo.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.osgi.api.PluginInfo;
+
+public class DefaultNodeInfo implements NodeInfo {
+
+    private final String nodeName;
+    private final DateTime bootTime;
+    private final DateTime updatedDate;
+    private final String killbillVersion;
+    private final String apiVersion;
+    private final String pluginApiVersion;
+    private final String commonVersion;
+    private final String platformVersion;
+    private final Iterable<PluginInfo> pluginInfo;
+
+    public DefaultNodeInfo(final String nodeName,
+                           final DateTime bootTime,
+                           final DateTime updatedDate,
+                           final String killbillVersion,
+                           final String apiVersion,
+                           final String pluginApiVersion,
+                           final String commonVersion,
+                           final String platformVersion,
+                           final Iterable<PluginInfo> pluginInfo) {
+        this.nodeName = nodeName;
+        this.bootTime = bootTime;
+        this.updatedDate = updatedDate;
+        this.killbillVersion = killbillVersion;
+        this.apiVersion = apiVersion;
+        this.pluginApiVersion = pluginApiVersion;
+        this.commonVersion = commonVersion;
+        this.platformVersion = platformVersion;
+        this.pluginInfo = pluginInfo;
+    }
+
+    @Override
+    public String getNodeName() {
+        return nodeName;
+    }
+
+    @Override
+    public DateTime getBootTime() {
+        return bootTime;
+    }
+
+    @Override
+    public DateTime getLastUpdatedDate() {
+        return updatedDate;
+    }
+
+    @Override
+    public String getKillbillVersion() {
+        return killbillVersion;
+    }
+
+    @Override
+    public String getApiVersion() {
+        return apiVersion;
+    }
+
+    @Override
+    public String getPlatformVersion() {
+        return platformVersion;
+    }
+
+    @Override
+    public String getCommonVersion() {
+        return commonVersion;
+    }
+
+    @Override
+    public String getPluginApiVersion() {
+        return pluginApiVersion;
+    }
+
+    @Override
+    public Iterable<PluginInfo> getPluginInfo() {
+        return pluginInfo;
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/info/KillbillInfoService.java b/util/src/main/java/org/killbill/billing/util/info/KillbillInfoService.java
new file mode 100644
index 0000000..1b36dc8
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/info/KillbillInfoService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info;
+
+import org.killbill.billing.platform.api.KillbillService;
+
+public interface KillbillInfoService extends KillbillService {
+}
diff --git a/util/src/main/java-templates/KillbillVersions.java b/util/src/main/java-templates/KillbillVersions.java
new file mode 100644
index 0000000..69af308
--- /dev/null
+++ b/util/src/main/java-templates/KillbillVersions.java
@@ -0,0 +1,31 @@
+package org.killbill.billing.util.info;
+
+public final class KillbillVersions {
+
+    private static final String KB_VERSION = "${killbill.version}";
+    private static final String API_VERSION = "${killbill-api.version}";
+    private static final String PLUGIN_API_VERSION = "${killbill-plugin-api.version}";
+    private static final String COMMON_VERSION = "${killbill-commons.version}";
+    private static final String PLATFORM_VERSION = "${killbill-platform.version}";
+
+    public static String getKillbillVersion() {
+        return KB_VERSION;
+    }
+
+    public static String getApiVersion() {
+        return API_VERSION;
+    }
+
+    public static String getPluginApiVersion() {
+        return PLUGIN_API_VERSION;
+    }
+
+    public static String getCommonVersion() {
+        return COMMON_VERSION;
+    }
+
+    public static String getPlatformVersion() {
+        return PLATFORM_VERSION;
+    }
+}
+
diff --git a/util/src/main/resources/org/killbill/billing/util/ddl.sql b/util/src/main/resources/org/killbill/billing/util/ddl.sql
index fb009d4..afcc4a1 100644
--- a/util/src/main/resources/org/killbill/billing/util/ddl.sql
+++ b/util/src/main/resources/org/killbill/billing/util/ddl.sql
@@ -288,3 +288,16 @@ CREATE TABLE roles_permissions (
     PRIMARY KEY(record_id)
 ) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
 CREATE INDEX roles_permissions_idx ON roles_permissions(role_name, permission);
+
+
+DROP TABLE IF EXISTS node_infos;
+CREATE TABLE node_infos (
+    record_id serial unique,
+    node_name varchar(50) NOT NULL,
+    boot_date datetime NOT NULL,
+    updated_date datetime DEFAULT NULL,
+    node_info text NOT NULL,
+    is_active boolean default true,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE UNIQUE INDEX node_name_idx ON node_infos(node_name);
diff --git a/util/src/main/resources/org/killbill/billing/util/info/dao/NodeInfoSqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/info/dao/NodeInfoSqlDao.sql.stg
new file mode 100644
index 0000000..312c45d
--- /dev/null
+++ b/util/src/main/resources/org/killbill/billing/util/info/dao/NodeInfoSqlDao.sql.stg
@@ -0,0 +1,71 @@
+group NodeInfoSqlDao;
+
+tableName() ::= "node_infos"
+
+tableFields(prefix) ::= <<
+  <prefix>node_name
+, <prefix>boot_date
+, <prefix>updated_date
+, <prefix>node_info
+, <prefix>is_active
+>>
+
+allTableFields(prefix) ::= <<
+  <prefix>record_id
+, <tableFields(prefix)>
+>>
+
+
+tableValues() ::= <<
+  :nodeName
+, :bootDate
+, :updatedDate
+, :nodeInfo
+, :isActive
+>>
+
+allTableValues() ::= <<
+  :recordId
+, <tableValues()>
+>>
+
+create() ::= <<
+insert into <tableName()> (
+<tableFields()>
+)
+values (
+<tableValues()>
+)
+;
+>>
+
+getByNodeName() ::= <<
+select <allTableFields()>
+from <tableName()>
+where node_name = :nodeName
+and is_active
+;
+>>
+
+getAll() ::= <<
+select <allTableFields()>
+from <tableName()>
+where is_active
+order by node_name asc
+;
+>>
+
+updateNodeInfo() ::= <<
+update <tableName()>
+set node_info = :nodeInfo
+, updated_date = :updatedDate
+where node_name = :nodeName
+;
+>>
+
+delete() ::= <<
+delete
+from <tableName()>
+where node_name = :nodeName
+;
+>>
\ No newline at end of file
diff --git a/util/src/test/java/org/killbill/billing/util/glue/TestUtilModuleWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/util/glue/TestUtilModuleWithEmbeddedDB.java
index 0b56059..ca72831 100644
--- a/util/src/test/java/org/killbill/billing/util/glue/TestUtilModuleWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/util/glue/TestUtilModuleWithEmbeddedDB.java
@@ -20,7 +20,15 @@ package org.killbill.billing.util.glue;
 
 import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import org.killbill.billing.api.TestApiListener;
+import org.killbill.billing.osgi.api.PluginInfo;
+import org.killbill.billing.osgi.api.PluginsInfoApi;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
+import org.killbill.billing.util.info.DefaultKillbillInfoApi;
+import org.killbill.billing.util.info.DefaultKillbillInfoService;
+import org.killbill.billing.util.info.KillbillInfoApi;
+import org.killbill.billing.util.info.KillbillInfoService;
+import org.mockito.Mockito;
 
 public class TestUtilModuleWithEmbeddedDB extends TestUtilModule {
 
@@ -34,6 +42,7 @@ public class TestUtilModuleWithEmbeddedDB extends TestUtilModule {
         install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
 
         install(new AuditModule(configSource));
+        install(new InfoModuleWithPluginInfoApi(configSource));
         install(new TagStoreModule(configSource));
         install(new CustomFieldModule(configSource));
         install(new NonEntityDaoModule(configSource));
@@ -50,6 +59,18 @@ public class TestUtilModuleWithEmbeddedDB extends TestUtilModule {
 
         protected void installSecurityService() {
         }
+    }
+
+    private static class InfoModuleWithPluginInfoApi extends InfoModule {
+
+        public InfoModuleWithPluginInfoApi(final KillbillConfigSource configSource) {
+            super(configSource);
+        }
+
+        protected void installUserApi() {
+            bind(PluginsInfoApi.class).toInstance(Mockito.mock(PluginsInfoApi.class));
+            super.installUserApi();
+        }
 
     }
 }
diff --git a/util/src/test/java/org/killbill/billing/util/info/dao/TestNodeInfoDao.java b/util/src/test/java/org/killbill/billing/util/info/dao/TestNodeInfoDao.java
new file mode 100644
index 0000000..3693a3c
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/util/info/dao/TestNodeInfoDao.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you 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.
+ */
+
+package org.killbill.billing.util.info.dao;
+
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestNodeInfoDao extends UtilTestSuiteWithEmbeddedDB {
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        DateTime now = clock.getUTCNow();
+
+        final DateTime initialBootTime1 = clock.getUTCNow().minusDays(1);
+        final NodeInfoModelDao node1 = new NodeInfoModelDao(-1L, "node1", initialBootTime1, now, "nodeInfo", true);
+        nodeInfoDao.create(node1);
+
+        List<NodeInfoModelDao> all = nodeInfoDao.getAll();
+        assertEquals(all.size(), 1);
+        assertEquals(all.get(0), node1);
+
+        final DateTime secondBootTime1 = clock.getUTCNow();
+        now = clock.getUTCNow().plusSeconds(1);
+        final NodeInfoModelDao newNode1 = new NodeInfoModelDao(-1L, "node1", secondBootTime1, now, "nodeInfo", true);
+        nodeInfoDao.create(newNode1);
+
+        all = nodeInfoDao.getAll();
+        assertEquals(all.size(), 1);
+        assertEquals(all.get(0), newNode1);
+
+
+        final DateTime initialBootTime2 = clock.getUTCNow();
+        final NodeInfoModelDao node2 = new NodeInfoModelDao(-1L, "node2", initialBootTime2, now, "nodeInfo", true);
+        nodeInfoDao.create(node2);
+
+        all = nodeInfoDao.getAll();
+        assertEquals(all.size(), 2);
+        assertEquals(all.get(0), newNode1);
+        assertEquals(all.get(1), node2);
+
+        final NodeInfoModelDao newNode2 = new NodeInfoModelDao(-1L, "node2", initialBootTime2, now, "nodeInfo2", true);
+
+        nodeInfoDao.updateNodeInfo(newNode2.getNodeName(), newNode2.getNodeInfo());
+
+        all = nodeInfoDao.getAll();
+        assertEquals(all.size(), 2);
+        assertEquals(all.get(0), newNode1);
+        assertEquals(all.get(1), newNode2);
+    }
+
+}
diff --git a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
index 6bdbbb3..971042a 100644
--- a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
@@ -32,6 +32,7 @@ import org.killbill.billing.util.customfield.dao.CustomFieldDao;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.export.dao.DatabaseExportDao;
 import org.killbill.billing.util.glue.TestUtilModuleWithEmbeddedDB;
+import org.killbill.billing.util.info.dao.NodeInfoDao;
 import org.killbill.billing.util.tag.dao.DefaultTagDao;
 import org.killbill.billing.util.tag.dao.TagDefinitionDao;
 import org.killbill.bus.api.PersistentBus;
@@ -87,6 +88,8 @@ public abstract class UtilTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
     protected SecurityApi securityApi;
     @Inject
     protected SecurityConfig securityConfig;
+    @Inject
+    protected NodeInfoDao nodeInfoDao;
 
     @BeforeClass(groups = "slow")
     public void beforeClass() throws Exception {