/******************************************************************************* * Copyright (c) 2011 Freescale Semiconductor. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Serge Beauchamp (Freescale Semiconductor) - initial API and implementation *******************************************************************************/ package com.freescale.deadlockpreventer.agent; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import org.eclipse.ui.IMemento; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.XMLMemento; import com.freescale.deadlockpreventer.IContext; import com.freescale.deadlockpreventer.ILock; import com.freescale.deadlockpreventer.QueryService; import com.freescale.deadlockpreventer.QueryService.IBundleInfo; public class XMLUtil { private static final String THREAD = "thread"; private static final String CONTEXT = "context"; private static final String FOLLOWERS = "followers"; private static final String PRECEDENTS = "precedents"; private static final String VALUE = "value"; private static final String FRAME = "frame"; private static final String STACK = "stack"; private static final String ID = "id"; private static final String LOCK = "lock"; private static final String LOCKS = "locks"; private static final String BUNDLE_INFOS = "bundle-infos"; private static final String BUNDLE_INFO = "bundle-info"; private static final String PACKAGES = "packages"; private static final String PACKAGE = "package"; private static final String NAME = "name"; static public void write(IMemento root, ILock lock) { IMemento child = root.createChild(LOCK); child.putString(ID, lock.getID()); writeStackTrace(child, lock.getStackTrace()); writeContext(child, lock.getPrecedents(), PRECEDENTS); writeContext(child, lock.getFollowers(), FOLLOWERS); } private static void writeContext(IMemento child, IContext[] contextes, String label) { IMemento precedents = child.createChild(label); for (IContext context : contextes) { IMemento precedent = precedents.createChild(CONTEXT); precedent.putString(THREAD, context.getThreadID()); precedent.putString(ID, context.getLock().getID()); writeStackTrace(precedent, context.getStackTrace()); } } private static void writeStackTrace(IMemento child, String[] stackTrace) { IMemento stack = child.createChild(STACK); for (int i = 0; i < stackTrace.length; i++) { IMemento frame = stack.createChild(FRAME); frame.putString(VALUE, stackTrace[i]); } } public static ILock[] readLocks(IMemento memento) throws WorkbenchException { // We use a stringTable to make sure that the strings in the data structures // are all part of a string pool. Otherwise, it will consume very large amount of // memory. HashMap<String, String> stringTable = new HashMap<String, String>(); HashMap<String, ILock> result = new HashMap<String, ILock>(); IMemento locks = memento.getChild(LOCKS); for (IMemento lock : locks.getChildren(LOCK)) { String id = getString(stringTable, lock.getString(ID)); String[] stackTrace = getStackTrace(stringTable, lock); IContext[] precedents = getContext(stringTable, lock, PRECEDENTS); IContext[] followers = getContext(stringTable, lock, FOLLOWERS); ILock newLock = new Lock(id, stackTrace, precedents, followers); result.put(id, newLock); } resolvePlaceHolders(result); return result.values().toArray(new ILock[0]); } private static void resolvePlaceHolders(HashMap<String, ILock> map) { for (ILock lock : map.values()) { resolvePlaceHolders(map, lock.getPrecedents()); resolvePlaceHolders(map, lock.getFollowers()); } } private static void resolvePlaceHolders(HashMap<String, ILock> map, IContext[] contexes) { for (IContext context : contexes) { ILock lock = map.get(context.getLock().getID()); if (lock != null) ((Context) context).setLock(lock); } } private static IContext[] getContext(HashMap<String, String> stringTable, IMemento lock, String label) { ArrayList<IContext> contexes = new ArrayList<IContext>(); IMemento contextList = lock.getChild(label); for (IMemento context : contextList.getChildren(CONTEXT)) { String lockId = getString(stringTable, context.getString(ID)); String thread = getString(stringTable, context.getString(THREAD)); String[] stackTrace = getStackTrace(stringTable, context); contexes.add(new Context(thread, new Lock(lockId), stackTrace)); } return contexes.toArray(new IContext[0]); } private static String[] getStackTrace(HashMap<String, String> stringTable, IMemento lock) { ArrayList<String> stackTrace = new ArrayList<String>(); IMemento stack = lock.getChild(STACK); for (IMemento frame : stack.getChildren(FRAME)) { stackTrace.add(getString(stringTable, frame.getString(VALUE))); } return stackTrace.toArray(new String[0]); } private static String getString(HashMap<String, String> stringTable, String string) { String result = stringTable.get(string); if (result == null) { stringTable.put(string, string); result = string; } return result; } public static void write(XMLMemento root, Collection<IBundleInfo> values) { IMemento child = root.createChild(BUNDLE_INFOS); for (IBundleInfo info : values) { IMemento infoElement = child.createChild(BUNDLE_INFO); infoElement.putString(NAME, info.getName()); IMemento packagesElement = infoElement.createChild(PACKAGES); for (String packag : info.getPackages()) { IMemento pakageElement = packagesElement.createChild(PACKAGES); pakageElement.putString(VALUE, packag); } } } public static IBundleInfo[] readBundleInfos(IMemento memento) throws WorkbenchException { // We use a stringTable to make sure that the strings in the data structures // are all part of a string pool. Otherwise, it will consume very large amount of // memory. ArrayList<QueryService.IBundleInfo> result = new ArrayList<QueryService.IBundleInfo>(); IMemento locks = memento.getChild(BUNDLE_INFOS); for (IMemento bundleInfo : locks.getChildren(BUNDLE_INFO)) { String name = bundleInfo.getString(NAME); ArrayList<String> packages = new ArrayList<String>(); IMemento packagesElement = memento.getChild(PACKAGES); for (IMemento pkgElement : packagesElement.getChildren(PACKAGE)) { String value = pkgElement.getString(VALUE); packages.add(value); } IBundleInfo newBundleInfo = new BundleInfo(name, packages.toArray(new String[0])); result.add(newBundleInfo); } return result.toArray(new IBundleInfo[0]); } } class BundleInfo implements IBundleInfo { String name; String[] packages; public BundleInfo(String name, String[] packages) { this.name = name; this.packages = packages; } @Override public String getName() { return name; } @Override public String[] getPackages() { return packages; } } class Lock implements ILock { String id; String[] stackTrace; IContext[] precedents; IContext[] followers; public Lock(String id) { this.id = id; } public Lock(String id, String[] stackTrace, IContext[] precedents, IContext[] followers) { this.id = id; this.stackTrace = stackTrace; this.precedents = precedents; this.followers = followers; } @Override public String getID() { return id; } @Override public String[] getStackTrace() { return stackTrace; } @Override public IContext[] getPrecedents() { return precedents; } @Override public IContext[] getFollowers() { return followers; } @Override public ArrayList<String> serialize() { throw new RuntimeException(); } @Override public void serialize(ArrayList<String> result) { throw new RuntimeException(); } } class Context implements IContext { String thread; ILock lock; String[] stackTrace; public Context(String thread, ILock lock, String[] stackTrace) { this.thread = thread; this.lock = lock; this.stackTrace = stackTrace; } public void setLock(ILock lock) { this.lock = lock; } @Override public String getThreadID() { return thread; } @Override public String[] getStackTrace() { return stackTrace; } @Override public ILock getLock() { return lock; } }