* Put copy-on-write WrappedMap into separate class in order to be able to synchronize

all of its methods, which is necessary to catch the switch between original and copied map,
  and which isn't necessary for ordinary and read-only WrappedMaps.
This commit is contained in:
hns 2005-09-01 21:57:56 +00:00
parent d728d7fe21
commit 1a3434837d
3 changed files with 146 additions and 57 deletions

View file

@ -298,7 +298,7 @@ public class ApplicationBean implements Serializable {
* @return
*/
public Map getCronJobs() {
return new WrappedMap(app.customCronJobs, WrappedMap.READ_ONLY);
return new WrappedMap(app.customCronJobs, true);
}
/**
@ -397,8 +397,7 @@ public class ApplicationBean implements Serializable {
*/
public Map getProperties() {
if (properties == null) {
properties = new WrappedMap(app.getProperties());
properties.setReadonly(true);
properties = new WrappedMap(app.getProperties(), true);
}
return properties;
}

View file

@ -0,0 +1,132 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.util;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
/**
* A Map that wraps another map and creates a new copy of the
* wrapped map if we try to modify it. This class is wrapped
* as a native scripted object in JavaScript rather than exposing
* them through Java reflection.
*
* All methods in this class are synchronized in order not
* to miss the switch between original and copied map.
*/
public class CopyOnWriteMap extends WrappedMap {
boolean modified = false;
/**
* Constructor
*/
public CopyOnWriteMap(Map map) {
super(map);
}
public synchronized boolean wasModified() {
return modified;
}
public synchronized int size() {
return wrapped.size();
}
public synchronized boolean isEmpty() {
return wrapped.isEmpty();
}
public synchronized boolean containsKey(Object key) {
return wrapped.containsKey(key);
}
public synchronized boolean containsValue(Object value) {
return wrapped.containsValue(value);
}
public synchronized Object get(Object key) {
return wrapped.get(key);
}
// Modification Operations - check for readonly
public synchronized Object put(Object key, Object value) {
if (!modified) {
wrapped = new HashMap(wrapped);
modified = true;
}
return wrapped.put(key, value);
}
public synchronized Object remove(Object key) {
if (!modified) {
wrapped = new HashMap(wrapped);
modified = true;
}
return wrapped.remove(key);
}
public synchronized void putAll(Map t) {
if (!modified) {
wrapped = new HashMap(wrapped);
modified = true;
}
wrapped.putAll(t);
}
public synchronized void clear() {
if (!modified) {
wrapped = new HashMap(wrapped);
modified = true;
}
wrapped.clear();
}
// Views
public synchronized Set keySet() {
return wrapped.keySet();
}
public synchronized Collection values() {
return wrapped.values();
}
public synchronized Set entrySet() {
return wrapped.entrySet();
}
// Comparison and hashing
public synchronized boolean equals(Object o) {
return wrapped.equals(o);
}
public synchronized int hashCode() {
return wrapped.hashCode();
}
// toString
public synchronized String toString() {
return wrapped.toString();
}
}

View file

@ -19,58 +19,46 @@ package helma.util;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.HashMap;
/**
* A Map that wraps another map. We use this class to be able to
* wrap maps as native objects within a scripting engine rather
* than exposing them through Java reflection.
* Additionally, instances of this class can be set to readonly
* and copy-on-write so that the original map is left unchanged
* if the map contents are modified.
* so that the original map can't be modified.
*/
public class WrappedMap implements Map {
// the wrapped map
private Map wrapped = null;
protected Map wrapped = null;
// is this map readonly?
boolean readonly = false;
// isolate changes from the wrapped map?
boolean copyOnWrite = false;
public final static int READ_ONLY = 1;
public final static int COPY_ON_WRITE = 2;
protected boolean readonly = false;
/**
* Constructor
*/
public WrappedMap(Map map) {
if (map == null) {
throw new IllegalArgumentException(
"Argument must not be null in WrappedMap constructor");
}
wrapped = map;
this(map, false);
}
/**
* Constructor
*/
public WrappedMap(Map map, int mode) {
this(map);
if (mode == READ_ONLY) {
readonly = true;
} else if (mode == COPY_ON_WRITE) {
copyOnWrite = true;
public WrappedMap(Map map, boolean readonly) {
if (map == null) {
throw new NullPointerException(
"null Map passed to WrappedMap constructor");
}
wrapped = map;
this.readonly = readonly;
}
/**
* Set the readonly flag on or off
*/
public void setReadonly(boolean ro) {
readonly = ro;
public void setReadonly(boolean readonly) {
this.readonly = readonly;
}
/**
@ -80,20 +68,6 @@ public class WrappedMap implements Map {
return readonly;
}
/**
* Set the copyOnWrite flag on or off
*/
public void setCopyOnWrite(boolean cow) {
copyOnWrite = cow;
}
/**
* Is this map copyOnWrite?
*/
public boolean isCopyOnWrite() {
return copyOnWrite;
}
// Methods from interface java.util.Map -
// these are just proxies to the wrapped map, except for
// readonly checks on modifiers.
@ -124,10 +98,6 @@ public class WrappedMap implements Map {
if (readonly) {
throw new RuntimeException("Attempt to modify readonly map");
}
if (copyOnWrite) {
wrapped = new HashMap (wrapped);
copyOnWrite = false;
}
return wrapped.put(key, value);
}
@ -135,10 +105,6 @@ public class WrappedMap implements Map {
if (readonly) {
throw new RuntimeException("Attempt to modify readonly map");
}
if (copyOnWrite) {
wrapped = new HashMap (wrapped);
copyOnWrite = false;
}
return wrapped.remove(key);
}
@ -146,10 +112,6 @@ public class WrappedMap implements Map {
if (readonly) {
throw new RuntimeException("Attempt to modify readonly map");
}
if (copyOnWrite) {
wrapped = new HashMap (wrapped);
copyOnWrite = false;
}
wrapped.putAll(t);
}
@ -157,10 +119,6 @@ public class WrappedMap implements Map {
if (readonly) {
throw new RuntimeException("Attempt to modify readonly map");
}
if (copyOnWrite) {
wrapped = new HashMap (wrapped);
copyOnWrite = false;
}
wrapped.clear();
}