/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.config;
import com.ericsson.ssa.config.Config;
import com.ericsson.ssa.config.ConfigRuntimeException;
import com.ericsson.ssa.config.event.ConfigAddEvent;
import com.ericsson.ssa.config.event.ConfigEvent;
import com.ericsson.ssa.config.event.ConfigEventMulticaster;
import com.ericsson.ssa.config.event.ConfigRemoveEvent;
import com.ericsson.ssa.config.event.ConfigUpdateEvent;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
/**
* Replaces the original Config implementation with an in memory backing store.
*/
public class SimpleConfig implements Config {
private static String ADDITIONAL_PROPERTY_NODE="ElementProperty";
//The Preferences is not used as prefrences
//It is used as a tree structured collection of
//in memory properties.
private SimplePreferences prefs = new SimplePreferences();
//This is lagecy as the Preferences used now is not anymore shared
//by multiple instances in a cluster. Still kept the concept as it doesn't
//harm and may ease tracing in a cluster environement.
private String serverSpecificNode;
private Logger log = LogUtil.SIP_LOGGER.getLogger();
//Event and transaction
private ThreadLocal<List<ConfigEvent>> tlEventQueue = new ThreadLocal<List<ConfigEvent>>();
private ThreadLocal<String> tlEventMask = new ThreadLocal<String>();
private ThreadLocal<List<String>> tlEventTypes = new ThreadLocal<List<String>>();
private ConfigEventMulticaster eventMulticaster;
private ElementPropertyEventFilter elementPropertyEventFilter;
public SimpleConfig(String aServerSpecificNode) {
serverSpecificNode = aServerSpecificNode;
}
//Config
public String get(String key) {
String value = get(serverSpecificNode, key);
if (value==null) {
value = System.getProperty(key);
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Using extention mechanism: jvm-option, key ["+key+
"] -> "+value);
}
}
return value;
}
public String get(String node, String key) {
String value = null;
try {
if (prefs.nodeExists(node)) {
value = prefs.node(node).get(key, null);
if (value == null &&
prefs.node(node).nodeExists(ADDITIONAL_PROPERTY_NODE)) {
value=prefs.node(node).node(ADDITIONAL_PROPERTY_NODE).get(key, null);
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Using extention mechanism: additional property, key ["+key+
"] and node ["+node+"]-> "+value);
}
}
}
} catch (BackingStoreException ex) {
//Ignore
}
return value;
}
public Map<String, String> getAll(String node) {
List<String> keys = new ArrayList<String>();
try {
if (prefs.nodeExists(node)) {
keys.addAll(Arrays.asList(prefs.node(node).keys()));
//Also here make sure the extention mechanism is in place.
if (prefs.node(node).nodeExists(ADDITIONAL_PROPERTY_NODE)) {
String[] extKeys = prefs.node(node).node(ADDITIONAL_PROPERTY_NODE).keys();
for (String key : extKeys) {
if (!keys.contains(key)) {
keys.add(key);
}
}
}
}
} catch (BackingStoreException ex) {
//Ignore
}
Map<String, String> keyValues =
new HashMap<String, String>(keys.size());
for (String key : keys) {
keyValues.put(key, get(node, key));
}
return keyValues;
}
public String[] getChildNames(String node) {
String[] names = null;
try {
names = prefs.node(node).childrenNames();
} catch (BackingStoreException ex) {
//ignore shouldn't happen!
names = new String[] { };
}
return names;
}
public void set(String key, String value) {
set(serverSpecificNode, key, value);
}
public void set(String node, String key, String value) {
prefs.node(node).put(key, value);
}
public void clear() {
clear(serverSpecificNode);
}
public void clearAll() {
clear();
}
public void clear(String node) {
try {
prefs.node(node).clear();
} catch (BackingStoreException ex) {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "sip.integration.operation_failed_continue", new Object[] { getClass().getName(), "clear" });
log.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
public void remove(String node, String key) {
try {
prefs.node(node).remove(key);
prefs.flush();
} catch (BackingStoreException ex) {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "sip.integration.operation_failed_continue", new Object[] { getClass().getName(), "remove" });
log.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
public void remove(String node) {
try {
prefs.node(node).removeNode();
prefs.flush();
} catch (BackingStoreException ex) {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "sip.integration.operation_failed_continue", new Object[] { getClass().getName(), "remove" });
log.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
public void copyConfig(String fromNode, String toNode) {
try {
for (String key : prefs.node(fromNode).keys()) {
String value = prefs.node(fromNode).get(key, null);
prefs.node(toNode).put(key, value);
}
} catch (BackingStoreException ex) {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "sip.integration.operation_failed_continue", new Object[] { getClass().getName(), "copyConfig" });
log.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
//Event and transaction
public void setEventMulticaster(ConfigEventMulticaster anEventMulticaster) {
if (elementPropertyEventFilter==null) {
elementPropertyEventFilter = new ElementPropertyEventFilter();
eventMulticaster = elementPropertyEventFilter;
}
elementPropertyEventFilter.setEventMulticaster(anEventMulticaster);
}
class ElementPropertyEventFilter extends ConfigEventMulticaster {
private ConfigEventMulticaster delegate;
public void setEventMulticaster(ConfigEventMulticaster anEventMulticaster) {
delegate = anEventMulticaster;
}
@Override
public void processEvent(ConfigEvent event) {
ConfigEvent filteredEvent = event;
//Filtering is as such that any type of event on an ElementProperty
//node is translated to ConfigUpdateEvent on the parent node.
//Which would be the same behaviour as the ElementProperty nodes
//single key was a key on the parent.
if (event.getNode().endsWith("/"+ADDITIONAL_PROPERTY_NODE)) {
String parent = event.getNode().substring(0,
event.getNode().length()-
(1+ADDITIONAL_PROPERTY_NODE.length()));
filteredEvent = new ConfigUpdateEvent(SimpleConfig.this, parent);
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Translating ElementProperty Event "+
"from [type="+event.getType()+", node="+event.getNode()+"]"+
", to [type="+filteredEvent.getType()+", node="+filteredEvent.getNode()+"]");
}
}
delegate.processEvent(filteredEvent);
}
}
public void nodeAdded(String parentNode, String childNode) {
List<ConfigEvent> currentEventQueue = tlEventQueue.get();
if (currentEventQueue != null) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Capturing nodeAdded parentNode = " +
parentNode + ", childNode = " + childNode + ", eventMask = " +
tlEventMask.get() + ", eventTypes = " + tlEventTypes.get());
}
if ((tlEventTypes.get() == null) ||
tlEventTypes.get().contains(ConfigAddEvent.TYPE)) {
if ((tlEventMask.get() == null) ||
parentNode.matches(tlEventMask.get())) {
ConfigAddEvent configEvent = new ConfigAddEvent(this,
parentNode, childNode);
if (!currentEventQueue.contains(configEvent)) {
currentEventQueue.add(configEvent);
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Captured event " +
configEvent.getType() + " : " +
configEvent.getNode() + " mask: " +
tlEventMask.get() + " types: " +
tlEventTypes.get());
}
}
}
}
}
}
public void nodeChanged(String node) {
List<ConfigEvent> currentEventQueue = tlEventQueue.get();
if (currentEventQueue != null) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Capturing nodeChanged node = " + node +
", eventMask = " + tlEventMask.get() + ", eventTypes = " +
tlEventTypes.get());
}
if ((tlEventTypes.get() == null) ||
tlEventTypes.get().contains(ConfigUpdateEvent.TYPE)) {
if ((tlEventMask.get() == null) ||
node.matches(tlEventMask.get())) {
ConfigUpdateEvent configEvent = new ConfigUpdateEvent(this,
node);
if (!currentEventQueue.contains(configEvent)) {
currentEventQueue.add(configEvent);
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Captured event " +
configEvent.getType() + " : " +
configEvent.getNode() + " mask: " +
tlEventMask.get() + " types: " +
tlEventTypes.get());
}
} else {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Skipped duplicate event " +
configEvent.getType() + " : " +
configEvent.getNode() + " mask: " +
tlEventMask.get() + " types: " +
tlEventTypes.get());
}
}
}
}
}
}
public void nodeRemoved(String parentNode, String childNode) {
List<ConfigEvent> currentEventQueue = tlEventQueue.get();
if (currentEventQueue != null) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Capturing nodeRemoved parentNode = " +
parentNode + ", childNode = " + childNode + ", eventMask = " +
tlEventMask.get() + ", eventTypes = " + tlEventTypes.get());
}
if ((tlEventTypes.get() == null) ||
tlEventTypes.get().contains(ConfigRemoveEvent.TYPE)) {
if ((tlEventMask.get() == null) ||
parentNode.matches(tlEventMask.get())) {
ConfigRemoveEvent configEvent = new ConfigRemoveEvent(this,
parentNode, childNode);
if (!currentEventQueue.contains(configEvent)) {
currentEventQueue.add(configEvent);
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Captured event " +
configEvent.getType() + " : " +
configEvent.getNode() + " mask: " +
tlEventMask.get() + " types: " +
tlEventTypes.get());
}
}
}
}
}
}
public void captureEvents() {
captureEvents(null, null);
}
public void captureEvents(String eventMask) {
captureEvents(eventMask, null);
}
public void captureEvents(String[] eventTypes) {
captureEvents(null, eventTypes);
}
public void captureEvents(String eventMask, String[] eventTypes) {
tlEventQueue.set(new ArrayList<ConfigEvent>());
tlEventMask.set(eventMask);
if (eventTypes != null) {
tlEventTypes.set(Arrays.asList(eventTypes));
}
}
public void processEvents() {
List<ConfigEvent> currentEventQueue = tlEventQueue.get();
ConfigRuntimeException troubles = null;
if (currentEventQueue != null) {
try {
for (ConfigEvent event : currentEventQueue) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "processing event: [" +
event.getClass().getName() + ", " + event.getNode() +
"]");
}
try {
eventMulticaster.processEvent(event);
} catch(Exception e) {
//This catch may be to wide!
//Each event gets the opertunaty be handled and
//lokalize the effect of troublesome listeners.
if (troubles==null) {
troubles = new ConfigRuntimeException("Processing events failed for atleast one event.");
}
troubles.addCause(e);
}
}
} finally {
currentEventQueue.clear();
tlEventQueue.set(null);
}
}
if (troubles!=null) {
throw troubles;
}
}
//Support
@Override
public String toString() {
StringBuffer buffer = new StringBuffer("Config[" + hashCode() + "]: ");
buffer.append(prefs.toString());
return buffer.toString();
}
//Custom Preferences that just is a in memory backing store.
class SimplePreferences extends AbstractPreferences {
HashMap<String, String> preferences = new HashMap<String, String>();
HashMap<String, SimplePreferences> children = new HashMap<String, SimplePreferences>();
public SimplePreferences() {
super(null, "");
}
public SimplePreferences(SimplePreferences parent, String name) {
super(parent, name);
}
protected void putSpi(String key, String value) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "SimplePreferences[" + hashCode() +
"] putSpi called: [" + key + ", " + value + "]");
}
String oldValue = preferences.get(key);
preferences.put(key, value);
if (((value != null) && !value.equals(oldValue)) ||
((value == null) && (oldValue != null))) {
nodeChanged(absolutePath());
}
}
protected String getSpi(String key) {
return preferences.get(key);
}
protected void removeSpi(String key) {
preferences.remove(key);
nodeChanged(absolutePath());
}
protected void removeNodeSpi() throws BackingStoreException {
preferences.clear();
children.clear();
if (parent() != null) {
((SimplePreferences) parent()).removeChild(this);
}
}
protected String[] keysSpi() throws BackingStoreException {
return preferences.keySet()
.toArray(new String[preferences.keySet().size()]);
}
protected String[] childrenNamesSpi() throws BackingStoreException {
return children.keySet()
.toArray(new String[children.keySet().size()]);
}
protected AbstractPreferences childSpi(String name) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "SimplePreferences[" + hashCode() +
"] childSpi called: [" + name + "]\n"+ toString());
}
SimplePreferences child = children.get(name);
if (child == null) {
child = new SimplePreferences(this, name);
children.put(name, child);
}
nodeAdded(absolutePath(), child.absolutePath());
return child;
}
protected void syncSpi() throws BackingStoreException {
}
protected void flushSpi() throws BackingStoreException {
}
private void removeChild(SimplePreferences child) {
children.remove(child.name());
nodeRemoved(absolutePath(), child.absolutePath());
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer("SimplePreference[" +
hashCode() + "]: \n");
toString(buffer, "");
buffer.append("\n");
return buffer.toString();
}
public String toString(StringBuffer buffer, String indent) {
buffer.append(indent);
buffer.append("<" + ((name().length() != 0) ? name() : "root") +
">");
for (SimplePreferences child : children.values()) {
buffer.append("\n");
child.toString(buffer, indent + " ");
}
if (!preferences.isEmpty()) {
buffer.append("\n ");
buffer.append(indent);
buffer.append(preferences.toString());
}
buffer.append("\n");
buffer.append(indent);
buffer.append("</" + ((name().length() != 0) ? name() : "root") +
">");
return buffer.toString();
}
}
}