diff options
Diffstat (limited to 'ncomp-utils-journaling')
16 files changed, 1724 insertions, 0 deletions
diff --git a/ncomp-utils-journaling/.classpath b/ncomp-utils-journaling/.classpath new file mode 100644 index 0000000..79ccca7 --- /dev/null +++ b/ncomp-utils-journaling/.classpath @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" output="target/classes" path="src/main/java"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" output="target/test-classes" path="src/test/java"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="output" path="target/classes"/> +</classpath> diff --git a/ncomp-utils-journaling/.gitignore b/ncomp-utils-journaling/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/ncomp-utils-journaling/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/ncomp-utils-journaling/.project b/ncomp-utils-journaling/.project new file mode 100644 index 0000000..ee0b752 --- /dev/null +++ b/ncomp-utils-journaling/.project @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>ncomp-utils-journaling</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.m2e.core.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.m2e.core.maven2Nature</nature> + <nature>org.eclipse.pde.PluginNature</nature> + </natures> +</projectDescription> diff --git a/ncomp-utils-journaling/.settings/org.eclipse.core.resources.prefs b/ncomp-utils-journaling/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..e9441bb --- /dev/null +++ b/ncomp-utils-journaling/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding/<project>=UTF-8 diff --git a/ncomp-utils-journaling/.settings/org.eclipse.jdt.core.prefs b/ncomp-utils-journaling/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..ec4300d --- /dev/null +++ b/ncomp-utils-journaling/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/ncomp-utils-journaling/.settings/org.eclipse.m2e.core.prefs b/ncomp-utils-journaling/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/ncomp-utils-journaling/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/ncomp-utils-journaling/LICENSE.txt b/ncomp-utils-journaling/LICENSE.txt new file mode 100644 index 0000000..30471b5 --- /dev/null +++ b/ncomp-utils-journaling/LICENSE.txt @@ -0,0 +1,22 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +ECOMP and OpenECOMP are trademarks and service marks of AT&T Intellectual Property. diff --git a/ncomp-utils-journaling/META-INF/MANIFEST.MF b/ncomp-utils-journaling/META-INF/MANIFEST.MF new file mode 100644 index 0000000..e469752 --- /dev/null +++ b/ncomp-utils-journaling/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: ncomp-utils-journaling +Bundle-Version: 0.1.0.qualifier +Export-Package: org.openecomp.ncomp.utils.journaling;uses:="org.apache.log4j,junit.framework,org.json" +Require-Bundle: org.eclipse.emf.ecore;bundle-version="2.10.2", + org.eclipse.emf.ecore.xmi;bundle-version="2.10.2", + org.junit;bundle-version="4.11.0", + org.apache.log4j;bundle-version="1.2.15", + org.apache.commons.cli, + org.apache.commons.compress;bundle-version="1.6.0", + ncomp-utils-java;bundle-version="0.1.0" +Bundle-Vendor: %providerName +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 diff --git a/ncomp-utils-journaling/build.properties b/ncomp-utils-journaling/build.properties new file mode 100644 index 0000000..a1ec8c4 --- /dev/null +++ b/ncomp-utils-journaling/build.properties @@ -0,0 +1,3 @@ +source.. = src/main/java/ +bin.includes = META-INF/,\ + . diff --git a/ncomp-utils-journaling/pom.xml b/ncomp-utils-journaling/pom.xml new file mode 100644 index 0000000..250a7e2 --- /dev/null +++ b/ncomp-utils-journaling/pom.xml @@ -0,0 +1,162 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.openecomp.ncomp.utils</groupId> + <artifactId>ncomp-utils-journaling</artifactId> + <version>0.1.0-SNAPSHOT</version> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.target>1.7</maven.compiler.target> + <maven.compiler.source>1.7</maven.compiler.source> + </properties> + + + <pluginRepositories> + <!-- Black Duck plugin dependencies --> + <pluginRepository> + <id>JCenter</id> + <name>JCenter Repository</name> + <url>http://jcenter.bintray.com</url> + </pluginRepository> + + <pluginRepository> + <id>Restlet</id> + <name>Restlet Repository</name> + <url>http://maven.restlet.com</url> + </pluginRepository> + </pluginRepositories> +<build> + <plugins> + <!-- blackduck maven plugin --> + <plugin> + <groupId>com.blackducksoftware.integration</groupId> + <artifactId>hub-maven-plugin</artifactId> + <version>1.4.0</version> + <inherited>false</inherited> + <configuration> + <hubProjectName>${project.name}</hubProjectName> + <outputDirectory>${project.basedir}</outputDirectory> + </configuration> + <executions> + <execution> + <id>create-bdio-file</id> + <phase>package</phase> + <goals> + <goal>createHubOutput</goal> + </goals> + </execution> + </executions> + </plugin> + <!-- site maven plugin --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-site-plugin</artifactId> + <version>3.6</version> + <dependencies> + <dependency> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-webdav-jackrabbit</artifactId> + <version>2.10</version> + </dependency> + </dependencies> + </plugin> + + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.1</version> + <configuration> + <source /> + <target /> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>1.2.17</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + <version>1.9</version> + </dependency> + + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <version>1.3</version> + </dependency> + + + <dependency> + <groupId>org.eclipse.emf</groupId> + <artifactId>org.eclipse.emf.ecore</artifactId> + <version>2.11.0-v20150123-0347</version> + </dependency> + <dependency> + <groupId>org.eclipse.emf</groupId> + <artifactId>org.eclipse.emf.common</artifactId> + <version>2.11.0-v20150123-0347</version> + </dependency> + <dependency> + <groupId>org.eclipse.emf</groupId> + <artifactId>org.eclipse.emf.ecore.xmi</artifactId> + <version>2.11.0-v20150123-0347</version> + </dependency> + <dependency> + <groupId>org.openecomp.ncomp.utils</groupId> + <artifactId>ncomp-utils-java</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + + <distributionManagement> + <repository> + <id>osecomp-nexus-releases</id> + <name>OSECOMP Release Repository</name> + <url>${openecomp.nexus.releases}</url> + </repository> + <snapshotRepository> + <id>osecomp-nexus-snapshots</id> + <name>OSECOMP Snapshot Repository</name> + <url>${openecomp.nexus.snapshots}</url> + </snapshotRepository> + <site> + <id>dcae-javadoc</id> + <url>${site.urlroot}/${project.artifactId}/${project.version}/</url> + </site> + </distributionManagement> + + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10.4</version> + <configuration> + <failOnError>false</failOnError> + <doclet>org.umlgraph.doclet.UmlGraphDoc</doclet> + <docletArtifact> + <groupId>org.umlgraph</groupId> + <artifactId>umlgraph</artifactId> + <version>5.6</version> + </docletArtifact> + <additionalparam>-views</additionalparam> + <useStandardDocletOptions>true</useStandardDocletOptions> + </configuration> + </plugin> + </plugins> + </reporting> + +</project> diff --git a/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingDateObject.java b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingDateObject.java new file mode 100644 index 0000000..466e671 --- /dev/null +++ b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingDateObject.java @@ -0,0 +1,131 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.utils.journaling; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import org.apache.log4j.Logger; +import org.json.JSONObject; + +public class JournalingDateObject<T extends JournalingObject> { + public static final Logger logger = Logger + .getLogger(JournalingDateObject.class); + private String rootDir; + private HashMap<Date, T> map = new HashMap<Date, T>(); + private HashMap<Date, Date> createdTime = new HashMap<Date, Date>(); + @SuppressWarnings("rawtypes") + private Class clazz; + + @SuppressWarnings("rawtypes") + public JournalingDateObject(String filePrefix, Class clazz) { + this.rootDir = filePrefix; + this.clazz = clazz; + } + + public T get(Date d) { + return getPmap(d); + } + + public void save() { + synchronized (this) { + for (Date d : map.keySet()) { + map.get(d).save(); + } + Date now = new Date(); + List<Date> l = new ArrayList<Date>(); + for (Date d : map.keySet()) { + if (createdTime.get(d).getTime() + msInDay < now.getTime()) { + // created 1 day ago. remove from map to save memory + l.add(d); + } + } + for (Date d : l) { + map.remove(d); + createdTime.remove(d); + } + } + } + + public void close() { + save(); + for (Date d : map.keySet()) { + map.get(d).close(); + } + } + + private final long msInDay = 86400000L; + + @SuppressWarnings("unchecked") + private T getPmap(Date d) { + synchronized (this) { + Date d1 = new Date(d.getTime() / msInDay * msInDay); + T o = map.get(d1); + if (o == null) { + try { + @SuppressWarnings("rawtypes") + Constructor c = clazz.getConstructor(); + Method m = clazz.getMethod("create", File.class); + o = (T) m.invoke(c.newInstance(), dbRootDir(d1)); + map.put(d1, o); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + createdTime.put(d1, new Date()); + } + return o; + } + } + + private File dbRootDir(Date d) { + return new File(rootDir + String.format("/%1$tY_%1$tm_%1$td", d)); + } + + public void dump() { + synchronized (this) { + for (Date d : map.keySet()) { + T o = map.get(d); + System.out.println("Status for: " + d + " " + o); + } + } + } + + public JSONObject toJson() { + synchronized (this) { + JSONObject json = new JSONObject(); + for (Date d : map.keySet()) { + JSONObject json1 = new JSONObject(); + json1.put("createdTime", createdTime.get(d)); + json1.put("object", map.get(d).toJson()); + json.put(d.toString(), json1); + } + return json; + } + } + +} diff --git a/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingEvent.java b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingEvent.java new file mode 100644 index 0000000..cdf3224 --- /dev/null +++ b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingEvent.java @@ -0,0 +1,49 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.utils.journaling; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class JournalingEvent implements Serializable { + @Override + public String toString() { + return "JournalingEvent [context=" + context + ", o=" + value + ", method=" + method + "]"; + } + private static final long serialVersionUID = -7697839584139633393L; + List<String> context = new ArrayList<String>(); + Object value = null; + int method = -1; + public String pname; + public JournalingEvent() {} + public JournalingEvent(String context, int method, String pname, Object o) { + if (context != null) + this.context.add(context); + this.value = o; + this.method = method; + this.pname = pname; + } + public void addContext(JournalingObject journalingObject) { + context.add(journalingObject.getContext()); + } +} diff --git a/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingHashMap.java b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingHashMap.java new file mode 100644 index 0000000..3fab2ec --- /dev/null +++ b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingHashMap.java @@ -0,0 +1,243 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.utils.journaling; + +import java.io.File; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.json.JSONObject; + +public class JournalingHashMap<V> extends JournalingObject implements Map<String, V>, Serializable { + @Override + public String toString() { + return "JournalingHashMap [map=" + map + "]"; + } + + private static final long serialVersionUID = 1L; + private HashMap<String, V> map; + static final int REMOVE_METHOD = 2000; + static final int PUT_METHOD = 2001; + static final int CLEAR_METHOD = 2002; + static final int NEWKEY_METHOD = 2003; + + @SuppressWarnings("rawtypes") + public static JournalingHashMap create(File dir) { + return (JournalingHashMap) create2(dir, new JournalingHashMap()); + } + + public JournalingHashMap(String context, JournalingObject parent) { + super(context, parent); + } + + public JournalingHashMap() { + // TODO Auto-generated constructor stub + } + + @Override + public void init() { + if (map == null) + map = new HashMap<String, V>(); + else { + for (String k : map.keySet()) { + V o = map.get(k); + if (o instanceof JournalingObject) { + JournalingObject oo = (JournalingObject) o; + initChild(k, oo); + } + } + } + + } + @SuppressWarnings("unchecked") + void play(JournalingEvent e, int index) { + if (index == 0) { + switch (e.method) { + case REMOVE_METHOD: { + remove(e.pname); + return; + } + case PUT_METHOD: { + put(e.pname, (V) e.value); + return; + } + case CLEAR_METHOD: { + clear(); + return; + } + case NEWKEY_METHOD: { + try { + newKey(e.pname, (Class<V>) Class.forName((String) e.value)); + } catch (ClassNotFoundException e1) { + throw new RuntimeException("Unexpected class: " + e.method); + } + return; + } + default: + logger.debug("Unexpected method: " + e.method); + return; + } + } + String context = e.context.get(index - 1); + V c = map.get(context); + if (c == null) { + throw new RuntimeException("Unknown key: " + context + " not in " + map.keySet()); + } + JournalingObject cc = (JournalingObject) c; + cc.play(e, index - 1); + } + + public V newKey(String key, Class<V> clazz) { + return newKey2(key, clazz); + } + + public V newList(String key) { + return newKey2(key, JournalingList.class); + } + + public V newMap(String key) { + return newKey2(key, JournalingHashMap.class); + } + + private V newKey2(String key, @SuppressWarnings("rawtypes") Class clazz) { + try { + if (clazz.getName().contains("$")) + throw new RuntimeException("Cannot be an inner class: " + clazz.getName()); + @SuppressWarnings("unchecked") + Constructor<V> m = clazz.getConstructor(String.class, JournalingObject.class); + V v = m.newInstance(key, this); + log(new JournalingEvent(null, NEWKEY_METHOD, key, clazz.getName())); + map.put(key, v); + return v; + } catch (Exception e) { + throw new RuntimeException("Unable to create new " + clazz.getName() + " " + e); + } + } + + protected String eventToString(JournalingEvent e) { + switch (e.method) { + case REMOVE_METHOD: + return "map:remove " + e.context + " " + e.pname; + case PUT_METHOD: + return "map:put " + e.context + " " + e.pname + " " + e.value; + case CLEAR_METHOD: + return "map:put " + e.context; + case NEWKEY_METHOD: + return "map:new " + e.context + " key=" + e.pname + " " + e.value; + default: + return "map:def " + e.context + " key=" + e.pname + " value=" + e.value; + } + } + + // //////////////////// Standard Map Method ////////////////////////////// + @Override + public void clear() { + log(new JournalingEvent(null, CLEAR_METHOD, null, null)); + map.clear(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsKey(value); + } + + @Override + public Set<java.util.Map.Entry<String, V>> entrySet() { + return map.entrySet(); + } + + @Override + public V get(Object key) { + return map.get(key); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public Set<String> keySet() { + return map.keySet(); + } + + @Override + public V put(String key, V value) { + if (value instanceof JournalingObject) { + throw new RuntimeException("Use newkey method instead of put for values of type JournalingObject"); + } + log(new JournalingEvent(null, PUT_METHOD, key, value)); + return map.put(key, value); + } + + @Override + public void putAll(Map<? extends String, ? extends V> m) { + for (String k : m.keySet()) { + map.put(k, m.get(k)); + } + } + + @Override + public V remove(Object key) { + if (key == null || key instanceof String) { + String k = (String) key; + log(new JournalingEvent(null, REMOVE_METHOD, k, null)); + return map.remove(key); + } + return null; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public Collection<V> values() { + return map.values(); + } + + public JSONObject toJson() { + JSONObject json = new JSONObject(); + for (String k : map.keySet()) { + Object o = map.get(k); + if (o instanceof JournalingObject) { + JournalingObject o1 = (JournalingObject) o; + json.put(k, o1.toJson()); + } + else { + json.put(k, o.toString()); + } + } + return json; + } + +} diff --git a/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingList.java b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingList.java new file mode 100644 index 0000000..e40f842 --- /dev/null +++ b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingList.java @@ -0,0 +1,303 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.utils.journaling; + +import java.io.File; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class JournalingList<V> extends JournalingObject implements List<V>, Serializable { + @Override + public String toString() { + return "JournalingList [list=" + list + "]"; + } + + private static final long serialVersionUID = 1L; + private List<V> list; + static final int ADD_METHOD = 1001; + static final int CLEAR_METHOD = 1002; + static final int ADDNEW_METHOD = 1003; + static final int REMOVE_METHOD = 1004; + + + @SuppressWarnings("rawtypes") + static public JournalingList create(File dir) { + return (JournalingList) create2(dir,new JournalingList()); + } + + public JournalingList(String context, JournalingObject parent) { + super(context,parent); + } + public JournalingList() { + // TODO Auto-generated constructor stub + } + + @Override + public void init() { + if (list == null) + list = new ArrayList<V>(); + else { + int i = 0; + for (V o : list) { + if (o instanceof JournalingObject) { + JournalingObject oo = (JournalingObject) o; + String key = Integer.toString(i++); + initChild(key, oo); + } + } + } + } + + @SuppressWarnings("unchecked") + void play(JournalingEvent e,int index) { + if (index == 0) { + switch (e.method) { + case ADD_METHOD: { + add((V) e.value); + return; + } + case CLEAR_METHOD: { + clear(); + return; + } + case ADDNEW_METHOD: { + try { + addNew((Class<V>) Class.forName((String) e.value)); + } catch (ClassNotFoundException e1) { + throw new RuntimeException("Unexpected class: " + e.method); + } + return; + } + case REMOVE_METHOD: { + remove((int)(Integer)e.value); + return; + } + default: + logger.debug("Unexpected method: " + e.method); + return; + } + } + String context = e.context.get(index-1); + int i = Integer.parseInt(context); + V c = list.get(i); + if (c == null) { + throw new RuntimeException("unknown list error"); + } + JournalingObject cc = (JournalingObject) c; + cc.play(e, index-1); + } + protected String eventToString(JournalingEvent e) { + switch (e.method) { + case REMOVE_METHOD: + return "list:remove " + e.context + " " + e.value; + case ADD_METHOD: + return "list:add " + e.context + " " + e.value; + case CLEAR_METHOD: + return "list:clear " + e.context; + case ADDNEW_METHOD: + return "list:new " + e.context + " key=" + e.value; + default: + return "list:def " + e.context + " key=" + e.pname + " value=" + e.value; + } + } + + public V addNew(Class<V> clazz) { + return addNew2(clazz); + } + public V newList(String key) { + return addNew2(JournalingList.class); + } + public V newMap() { + return addNew2(JournalingHashMap.class); + } + private V addNew2(@SuppressWarnings("rawtypes") Class clazz) { + try { + if (clazz.getName().contains("$")) + throw new RuntimeException("Cannot be an inner class: " + clazz.getName()); + @SuppressWarnings("unchecked") + Constructor<V> m = clazz.getConstructor(String.class,JournalingObject.class); + String key = Integer.toString(list.size()); + V v = m.newInstance(key, this); + log(new JournalingEvent(null, ADDNEW_METHOD, null, clazz.getName())); + list.add(v); + return v; + } catch (Exception e) { + throw new RuntimeException("Unable to create new " + clazz.getName() + " " + e); + } + } + + ////////////////////// Standard List Method ////////////////////////////// + + @Override + public boolean add(V v) { + if (v instanceof JournalingObject) { + throw new RuntimeException("Use newkey method instead of put for values of type JournalingObject"); + } + log(new JournalingEvent(null, ADD_METHOD, null, v)); + return list.add(v); + } + + @Override + public void add(int arg0, V arg1) { + throw new RuntimeException("not implemented"); + } + + @Override + public boolean addAll(Collection<? extends V> l) { + for (V v: l) { + if (!add(v)) return false; + } + return true; + } + + @Override + public boolean addAll(int arg0, Collection<? extends V> arg1) { + throw new RuntimeException("not implemented"); + } + + @Override + public void clear() { + log(new JournalingEvent(null, CLEAR_METHOD, null, null)); + list.clear(); + } + + @Override + public boolean contains(Object o) { + return list.contains(o); + } + + @Override + public boolean containsAll(Collection<?> l) { + // TODO Auto-generated method stub + return list.containsAll(l); + } + + @Override + public V get(int index) { + return list.get(index); + } + + @Override + public int indexOf(Object o) { + return indexOf(o); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public Iterator<V> iterator() { + return list.iterator(); + } + + @Override + public int lastIndexOf(Object o) { + return list.lastIndexOf(o); + } + + @Override + public ListIterator<V> listIterator() { + return listIterator(); + } + + @Override + public ListIterator<V> listIterator(int i) { + return listIterator(i); + } + + @Override + public boolean remove(Object arg0) { + throw new RuntimeException("not implemented"); + } + + @Override + public V remove(int i) { + log(new JournalingEvent(null, REMOVE_METHOD, null, (Integer) i)); + return list.remove(i); + } + + @Override + public boolean removeAll(Collection<?> arg0) { + throw new RuntimeException("not implemented"); + } + + @Override + public boolean retainAll(Collection<?> arg0) { + throw new RuntimeException("not implemented"); + } + + @Override + public V set(int arg0, V arg1) { + throw new RuntimeException("not implemented"); + } + + @Override + public int size() { + return list.size(); + } + + @Override + public List<V> subList(int arg0, int arg1) { + throw new RuntimeException("not implemented"); + } + + @Override + public Object[] toArray() { + return list.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + return list.toArray(a); + } + + + + public JSONObject toJson() { + JSONObject json = new JSONObject(); + JSONArray a = new JSONArray(); + json.put("array", a); + for (V o : list) { + if (o instanceof JournalingObject) { + JournalingObject o1 = (JournalingObject) o; + a.put(o1.toJson()); + } + else { + a.put(o.toString()); + } + } + return json; + } + + +} diff --git a/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingObject.java b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingObject.java new file mode 100644 index 0000000..d294aa7 --- /dev/null +++ b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingObject.java @@ -0,0 +1,457 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.utils.journaling; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.StreamCorruptedException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.log4j.Logger; +import org.json.JSONObject; + +import org.openecomp.ncomp.webservice.utils.FileUtils; + +public abstract class JournalingObject { + public static final Logger logger = Logger.getLogger(JournalingObject.class); + static final int SET_METHOD = 0; + static final int SAVE_METHOD = -999; + protected HashMap<String, JournalingObject> children = new HashMap<String, JournalingObject>(); + private JournalingObject parent = null; + private String context; + private File dir; + private ObjectOutputStream jStream; + + private List<JournalingEvent> playList = new ArrayList<JournalingEvent>(); + private int snapShotInterval = 30 * 60000; // every 30 minutes + private Date lastSnapShot = new Date(); + private int numLogs = 0; + + static { + startCleanupThread(); + } + + public JournalingObject(String context, JournalingObject parent) { + setSnapshotInterval(snapShotInterval); + this.parent = parent; + this.context = context; + if (parent.children.get(context) != null) + throw new RuntimeException("dublicate child"); + parent.children.put(context, this); + init(); + } + + public JournalingObject() { + } + + public abstract void init(); + + static protected JournalingObject create2(File dir, JournalingObject o) { + if (dir.exists() && !dir.isDirectory()) + throw new RuntimeException("journaling directory exists but is not a directory"); + if (!dir.exists()) + dir.mkdirs(); + logger.info("creating journaling data structure: " + o.getClass().getName() + " " + dir); + File logFile = saveObjectFile(dir, "log.dat"); + File snapshotFile = FileUtils.createSafeFile(dir, "snapshot.dat"); + if (snapshotFile.exists()) { + JournalingObject oo = initFromSnapshot(snapshotFile); + if (oo != null) { + o = oo; + } + } + o.init(); // setup object + o.jStream = getObjectFile(dir, "log.dat"); + o.dir = dir; + if (logFile != null) { + o.initFromLog(logFile); + logger.info("initialized from file: " + logFile); + o.save(); + } + addCleanupDirectory(dir); + return o; + } + + protected void initChild(String context, JournalingObject child) { + children.put(context, child); + child.parent = this; + child.context = context; + child.init(); + } + + public synchronized void log(JournalingEvent event) { + if (parent != null) { + event.addContext(this); + parent.log(event); + return; + } + try { + if (logger.isDebugEnabled()) + logger.debug("log " + eventToString(event)); + jStream.writeUnshared(event); + numLogs++; + if (event.context.size() == 0 && event.method == SAVE_METHOD) { + jStream.flush(); + if (lastSnapShot.getTime() + snapShotInterval < new Date().getTime() && numLogs > 100) { + createSnapshot(); + } + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public void logAttributeValue(String arg, Object value) { + log(new JournalingEvent(null, SET_METHOD, arg, value)); + } + + public void save() { + if (parent != null) { + throw new RuntimeException("Can only save on the toplevel object"); + } + log(new JournalingEvent(null, SAVE_METHOD, null, null)); + try { + if (jStream != null) + jStream.reset(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void close() { + try { + jStream.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public String getContext() { + return context; + } + + private void play(JournalingEvent e) { + // System.out.println("play: " + e); + if (logger.isDebugEnabled()) + logger.debug("play: " + eventToString(e)); + if (e.context.size() == 0 && e.method == SAVE_METHOD) { + for (JournalingEvent e1 : playList) { + play(e1, e1.context.size()); + } + playList.clear(); + } else + playList.add(e); + } + + void play(JournalingEvent e, int index) { + if (index == 0) { + switch (e.method) { + case SET_METHOD: { + try { + Field fld = this.getClass().getDeclaredField(e.pname); + fld.setAccessible(true); + logAttributeValue(e.pname, e.value); + fld.set(this, e.value); + } catch (Exception e1) { + // TODO Auto-generated catch block + logger.error("Unable to set attribute: " + this.getClass().getName() + " " + e1); + logger.debug("Unable to set attribute: " + e.pname + " " + this.getClass().getName() + " " + e1); + } + return; + } + default: + throw new RuntimeException("Unexpected method: " + e.method); + } + } + String context = e.context.get(index - 1); + JournalingObject c = children.get(context); + if (c == null) { + throw new RuntimeException("Unknown Child: " + context + " not in " + children.keySet()); + } + c.play(e, index - 1); + } + + protected String eventToString(JournalingEvent e) { + switch (e.method) { + case SET_METHOD: + return "object:method context=" + e.context + " key=" + e.pname + " value=" + e.value; + default: + return e.toString(); + } + } + + void createSnapshot() { + Date now = new Date(); + String fName = "snapshot.dat"; + String tName = fName + "." + now.getTime(); + logger.debug("create snapshot:" + fName + " " + this); + try { + ObjectOutputStream out = getObjectFile(dir, tName); + try { + out.writeObject(this); + } finally { + out.close(); + } + File f = FileUtils.createSafeFile(dir, fName); + File t = FileUtils.createSafeFile(dir, tName); + if (f.exists()) { + f.delete(); + } + if (!t.renameTo(f)) { + throw new RuntimeException("Unable to rename file:" + f); + } + jStream.close(); + saveObjectFile(dir, "log.dat", now); + jStream = getObjectFile(dir, "log.dat"); + numLogs = 0; + lastSnapShot = new Date(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private static JournalingObject initFromSnapshot(File file) { + try { + logger.debug("reading" + file); + BufferedInputStream fin = new BufferedInputStream(new FileInputStream(file),16777216); + ObjectInputStream in = new ObjectInputStream(fin); + Object o = null; + try { + o = in.readObject(); + } finally { + in.close(); + } + return (JournalingObject) o; + } catch (Exception e) { + logger.error("Unable to init from snapshot file: " + file + " " + e); + if (logger.isDebugEnabled()) + e.printStackTrace(); + } + return null; + } + + public void setSnapshotInterval(int i) { + snapShotInterval = i; + lastSnapShot = new Date(); + // make sure snapshots time are randomized and not happening at the same time. + lastSnapShot.setTime(lastSnapShot.getTime()-(long) (i*Math.random())); + } + + public int getLogSize() { + return numLogs; + } + + @SuppressWarnings("static-access") + public static void main(String[] args) throws IOException, ClassNotFoundException { + CommandLineParser parser = new GnuParser(); + + // create the Options + Options options = new Options(); + options.addOption("h", "help", false, "Show usage"); + options.addOption(OptionBuilder.withLongOpt("file").withArgName("fileName").hasArg().create('f')); + // Handle inputs + CommandLine line = null; + try { + line = parser.parse(options, args); + } catch (ParseException e) { + } + if (line == null || line.hasOption("help") || line.hasOption("h")) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("util", options); + return; + } + // String args1[] = line.getArgs(); + if (line.hasOption("file")) { + FileInputStream fin = new FileInputStream(new File(line.getOptionValue("file"))); + ObjectInputStream in = new ObjectInputStream(fin); + try { + while (true) { + Object o; + try { + o = in.readUnshared(); + if (o instanceof JournalingEvent) { + JournalingEvent e = (JournalingEvent) o; + System.out.println(e.toString()); + continue; + } + System.out.println(o.toString()); + } catch (EOFException e) { + break; + } + } + } finally { + in.close(); + } + } + } + + static int num = 0; + + static private File saveObjectFile(File dir, String fname) { + return saveObjectFile(dir, fname, new Date()); + } + + static private File saveObjectFile(File dir, String fname, Date now) { + String fname2 = fname + "." + now.getTime() + "." + num++; + File f1 = FileUtils.createSafeFile(dir, fname); + File f2 = FileUtils.createSafeFile(dir, fname2); + if (f1.exists()) { + if (f2.exists()) { + f2.delete(); + } + if (!f1.renameTo(f2)) { + throw new RuntimeException("Unable to rename file: " + f2); + } + return f2; + } + return null; + } + + static private ObjectOutputStream getObjectFile(File dir, String fname) { + File f1 = FileUtils.createSafeFile(dir, fname); + ObjectOutputStream s = null; + try { + s = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f1))); + } catch (Exception e) { + throw new RuntimeException("unknown error: " + e); + } + return s; + } + + private void initFromLog(File file) { + int numEvents = 0; + Object o = null; + try { + FileInputStream fin = new FileInputStream(file); + ObjectInputStream in = new ObjectInputStream(fin); + try { + while (true) { + try { + o = in.readUnshared(); + // logger.info("new object: " + o); + numEvents++; + if (o instanceof JournalingEvent) { + JournalingEvent e = (JournalingEvent) o; + play(e); + } + if (o == null) { + logger.warn("read null object from: " + file); + } + } catch (EOFException e1) { + break; + } catch (StreamCorruptedException e1) { + break; + } + } + } finally { + if (in != null) + in.close(); + if (fin != null) + fin.close(); + } + playList.clear(); + } catch (EOFException e) { + logger.debug("initFromLog failed: " + file + " numEvents=" + numEvents + " o=" + o); + } catch (Exception e) { + logger.warn("initFromLog failed: " + file + " numEvents=" + numEvents + " o=" + o.getClass()); + logger.debug("initFromLog failed: " + file + " numEvents=" + numEvents + " o=" + o); + e.printStackTrace(); + } + } + + public JSONObject toJson() { + JSONObject json = new JSONObject(); + for (String k : children.keySet()) { + json.put(k, children.get(k).toJson()); + } + return json; + } + + // Cleanup old files + static List<File> cleanupDirectories = new ArrayList<File>(); + private static void startCleanupThread() { + Thread t = new Thread("journaling cleanup") { + public void run() { + while (true) { + try { + cleanup(); + Thread.sleep(300000); + } catch (Exception e) { + e.printStackTrace(); + logger.error(e); + } + } + } + + }; + t.setDaemon(true); + t.start(); + } + private static void cleanup() { + List<File> l = new ArrayList<File>(); + synchronized (cleanupDirectories) { + l.addAll(cleanupDirectories); + } + Date now = new Date(); + for (File dir : l) { + if (!dir.exists() || ! dir.isDirectory()) { + synchronized (cleanupDirectories) { + logger.warn("removing bad journaling directory" + dir.getAbsolutePath()); + cleanupDirectories.remove(dir); + continue; + } + } + for (File f : dir.listFiles()) { + // ignore file that is recently changed + if (f.lastModified() + 3600000 > now.getTime()) continue; + if (f.getName().startsWith("log.dat.")) + f.delete(); + } + } + } + private static void addCleanupDirectory(File dir) { + synchronized (cleanupDirectories) { + cleanupDirectories.add(dir); + } + } +} diff --git a/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingTest.java b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingTest.java new file mode 100644 index 0000000..a1e7517 --- /dev/null +++ b/ncomp-utils-journaling/src/main/java/org/openecomp/ncomp/utils/journaling/JournalingTest.java @@ -0,0 +1,255 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.utils.journaling; + +import java.io.File; +import java.io.Serializable; + +import junit.framework.TestCase; + +public class JournalingTest extends TestCase { + private static File clean(File f) { + if (f.isDirectory()) { + for (File x: f.listFiles()) { + clean(x); + } + } + f.delete(); + return(f); + } + @SuppressWarnings("unchecked") + public void test_case1() { + File f = clean(new File("testdata/journal/case1")); + JournalingHashMap<String> m = JournalingHashMap.create(f); + m.put("foo", "bar"); + m.save(); + m.close(); + for (int i = 0; i < 3; i++) { + // overwrite object with new. Can test that it has the correct + // state. + m = JournalingHashMap.create(f); + assertEquals("bar", m.get("foo")); + m.close(); + } + } + + public void test_case2() { + File f = clean(new File("testdata/journal/case2")); + MyTestJObject o = MyTestJObject.create(f); + o.setS("foo"); + o.setS("foo2"); + o.m.put("a", "b"); + o.l.add("foo1"); + o.l.add("foo2"); + o.l.add("foo3"); + o.l.remove(0); + o.save(); + o.setS("bar"); + o.close(); + for (int i = 0; i < 3; i++) { + o = MyTestJObject.create(f); + assertEquals("foo2", o.getS()); + assertEquals("b", o.m.get("a")); + assertEquals("foo2", o.l.get(0)); + o.close(); + } + } + + public void test_case3() { + File f = clean(new File("testdata/journal/case3")); + MyTestJObject2 o = MyTestJObject2.create(f); + o.setS("foo"); + // hashmap + MyTestJObject oo = o.m.newKey("x", MyTestJObject.class); + oo.m.put("a", "b"); + oo.setS("jjj"); + // list + MyTestJObject ooo = o.l.addNew(MyTestJObject.class); + ooo = o.l.addNew(MyTestJObject.class); + ooo.m.put("aa", "bb"); + ooo.setS("kkk"); + // hashmaplist + JournalingList<MyTestJObject> x = o.ml.newList("x"); + MyTestJObject xx = x.addNew(MyTestJObject.class); + xx.m.put("aa", "bb"); + xx.setS("kkk"); + o.save(); + o.setS("bar"); + o.close(); + for (int i = 0; i < 3; i++) { + // System.out.println("Round: " + i); + o = MyTestJObject2.create(f); + oo = o.m.get("x"); + assertEquals("foo", o.getS()); + assertEquals("jjj", oo.getS()); + assertEquals("b", oo.m.get("a")); + assertEquals(2, o.l.size()); + ooo = o.l.get(1); + assertEquals("kkk", ooo.getS()); + assertEquals("bb", ooo.m.get("aa")); + assertEquals(0, o.l.get(0).m.size()); + xx = o.ml.get("x").get(0); + assertEquals("kkk", xx.getS()); + assertEquals("bb", xx.m.get("aa")); + assertEquals(0, o.l.get(0).m.size()); + + o.close(); + } + } + + public void test_case4() { + File f = clean(new File("testdata/journal/case4")); + MyTestJObject o = MyTestJObject.create(f); + // o.setSnapshotInterval(1); // 1ms snapshots. This should force + // snapshots on each save. + o.setS("foo:1"); + o.setS("foo:2"); + o.save(); + // assertEquals(2,o.getLogSize()); + o.createSnapshot(); + // assertEquals(0,o.getLogSize()); + o.setS("foo:3"); + o.save(); + o.createSnapshot(); + o.l.add("foo1"); + o.save(); + o.close(); + + for (int i = 0; i < 3; i++) { + o = MyTestJObject.create(f); + System.out.println("Round: " + i + " " + o); + assertEquals("foo:3", o.getS()); + assertEquals("foo1", o.l.get(0)); + o.close(); + } + } + + public void test_case5() { + File f = clean(new File("testdata/journal/case5")); + MyTestJObject2 o = MyTestJObject2.create(f); + // o.setSnapshotInterval(1); // 1ms snapshots. This should force + // snapshots on each save. + o.setS("foo"); + o.save(); + for (int i = 0; i < 10; i++) { + o.setS("foo:" + i); + } + assertTrue(o.getLogSize() <= 2); + o.close(); + + for (int i = 0; i < 3; i++) { + o = MyTestJObject2.create(f); + System.out.println("Round: " + i + " " + o); + assertEquals("foo", o.getS()); + o.close(); + } + } +} + +class MyTestJObject extends JournalingObject implements Serializable { + @Override + public String toString() { + return "MyTestJObject [s=" + s + ", m=" + m + ", l=" + l + "]"; + } + + private static final long serialVersionUID = 1L; + private String s; + JournalingHashMap<String> m; + JournalingList<String> l; + + public MyTestJObject(String context, JournalingObject parent) { + super(context, parent); + } + + public MyTestJObject() { + // TODO Auto-generated constructor stub + } + + public String getS() { + return s; + } + + public void setS(String str) { + logAttributeValue("s", str); + this.s = str; + } + + @Override + public void init() { + if (m == null) + m = new JournalingHashMap<String>("m", this); + else + initChild("m", m); + if (l == null) + l = new JournalingList<String>("l", this); + else + initChild("l", l); + } + + static public MyTestJObject create(File dir) { + return (MyTestJObject) create2(dir, new MyTestJObject()); + } +} + +class MyTestJObject2 extends JournalingObject implements Serializable { + @Override + public String toString() { + return "MyTestJObject2 [s=" + s + ", m=" + m + ", l=" + l + ", ml=" + + ml + "]"; + } + + private static final long serialVersionUID = 1L; + private String s; + JournalingHashMap<MyTestJObject> m; + JournalingList<MyTestJObject> l; + JournalingHashMap<JournalingList<MyTestJObject>> ml; + + public String getS() { + return s; + } + + public void setS(String str) { + logAttributeValue("s", str); + this.s = str; + } + + @Override + public void init() { + if (m == null) + m = new JournalingHashMap<MyTestJObject>("m", this); + else + initChild("m", m); + if (l == null) + l = new JournalingList<MyTestJObject>("l", this); + else + initChild("l", l); + if (ml == null) + ml = new JournalingHashMap<JournalingList<MyTestJObject>>("ml", this); + else + initChild("ml", ml); + } + + static public MyTestJObject2 create(File dir) { + return (MyTestJObject2) create2(dir, new MyTestJObject2()); + } + +} |