/******************************************************************************* * Copyright (c) 2010 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; import java.io.DataInputStream; import java.io.IOException; import java.io.PrintStream; import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import com.freescale.deadlockpreventer.NetworkServer.Session; public class QueryService implements NetworkServer.IService { public static String ID = "query"; public static String VERSION_ID = "1.0.0"; private static String DUMP_LOCKS = "dump"; private static String INIT_TRANSACTION = "init_transaction"; private static String GET_LOCK = "get_lock"; private static String GET_BUNDLE_INFO = "get_bundle_info"; private static String CLOSE_TRANSACTION = "close_transaction"; private static IBuildInfoAdvisor bundleAdvisor = new IBuildInfoAdvisor() { @Override public String getBundleInfo(ClassLoader classloader, Class cls, ArrayList<String> packages) { return classloader.toString(); } }; private static boolean bundleAdvisorInitialized = false; public QueryService() { } public static class Client { NetworkServer.Session session; boolean connected = false; int lastTrasactionID = 0; class Transaction { public Transaction(int i) { id = Integer.toString(i); stats = new Statistics(); locks = stats.locks(); } String id; Statistics stats; ILock[] locks; } ArrayList<Transaction> transactions = new ArrayList<QueryService.Client.Transaction>(); public Client(String value) { try { session = NetworkServer.connect(value); connected = true; } catch (IOException e) { e.printStackTrace(); } } public void start() { Thread thread = new Thread(new Runnable() { @Override public void run() { Thread clientThread = new Thread(new ClientConnection(session)); clientThread.start(); } }); thread.start(); } public void stop() { session.close(); } private synchronized Message processQuery(Message query) { ArrayList<String> strs = query.getStrings(); if (strs.size() > 0) { if (strs.get(0).equals(DUMP_LOCKS)) { return new Message(new Statistics().serialize()); } if (strs.get(0).equals(INIT_TRANSACTION)) { Transaction transaction = new Transaction(++lastTrasactionID); transactions.add(transaction); ArrayList<String> response = new ArrayList<String>(); response.add(transaction.id); response.add(Integer.toString(transaction.locks.length)); response.addAll(transaction.stats.serializeStringTable()); return new Message(response); } if (strs.get(0).equals(CLOSE_TRANSACTION)) { String transactionID = strs.get(1); Transaction transaction = find(transactionID); if (transaction == null) return null; transactions.remove(transaction); return new Message(new String[0]); } if (strs.get(0).equals(GET_LOCK)) { String transactionID = strs.get(1); Transaction transaction = find(transactionID); if (transaction == null) return null; int index = Integer.parseInt(strs.get(2)); if (index < 0 || index >= transaction.locks.length) return null; int end = Integer.parseInt(strs.get(3)); if (end < 0 || end > transaction.locks.length || end < index) return null; ILock[] locks = Arrays.copyOfRange(transaction.locks, index, end); if (locks == null) return null; return new Message(Statistics.serializeLocks(locks)); } if (strs.get(0).equals(GET_BUNDLE_INFO)) { String transactionID = strs.get(1); Transaction transaction = find(transactionID); if (transaction == null) return null; // String lockId = strs.get(2); ArrayList<String> packages = new ArrayList<String>(); String name = getBundleInfo(strs.get(3), packages); return new Message(Statistics.serializeBundleInfo(name, packages.toArray(new String[0]))); } } return null; } private Transaction find(String transactionID) { for (Transaction trans : transactions) { if (trans.id.equals(transactionID)) return trans; } return null; } class ClientConnection implements Runnable{ NetworkServer.Session session; public ClientConnection(NetworkServer.Session session) { this.session = session; } @Override public void run() { DataInputStream input = session.getInput(); PrintStream output = session.getOutput(); Message message = new Message(); while(true) { try { message.readMessage(input); Message result = processQuery(message); if (result != null) { message.sendOK(output); result.respond(output); } else message.sendError(output, "error"); } catch (SocketException e) { break; } catch (IOException e) { try { message.sendError(output, e.toString()); } catch (IOException e1) { e1.printStackTrace(); } } } } } } ServerConnection connection = null; public boolean isConnected() { synchronized(this) { return connection != null; } } static public String getBundleInfo(String acquisitionRoot, ArrayList<String> packages) { // the acquisition root is a method, format "package.class.method" int index = acquisitionRoot.lastIndexOf("."); if (index != -1) acquisitionRoot = acquisitionRoot.substring(0, index); try { Class cls = Class.forName(acquisitionRoot); if (cls != null) { ClassLoader loader = cls.getClassLoader(); if (loader != null) { initializeBundleAdvisor(); return bundleAdvisor.getBundleInfo(loader, cls, packages); } } } catch (ClassNotFoundException e) { } return ""; } public static interface IBuildInfoAdvisor { public String getBundleInfo(ClassLoader classloader, Class cls, ArrayList<String> packages); } private static void initializeBundleAdvisor() { if (!bundleAdvisorInitialized) { bundleAdvisorInitialized = true; String bundleAdvisorClass = System.getProperty(Settings.BUNDLE_ADVISOR); if (bundleAdvisorClass != null) { try { Class<?> cls = Class.forName(bundleAdvisorClass); if (cls.isInstance(IBuildInfoAdvisor.class)) bundleAdvisor = (IBuildInfoAdvisor) cls.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } public boolean isClosed() { synchronized(this) { return connection != null && connection.isClosed(); } } /** * Get the complete list of locks. Can be very slow for large programs. * @return the complete list of locks. */ public ILock[] getLocks() { Message msg = new Message(new ArrayList<String>(Arrays.asList(new String[] {DUMP_LOCKS}))); Message result = connection.request(msg); if (result == null) return null; return new Statistics(new LinkedList<String>(result.getStrings())).locks(); } public ITransaction createTransaction() { Message msg = new Message(new ArrayList<String>(Arrays.asList(new String[] {INIT_TRANSACTION}))); Message result = connection.request(msg); if (result == null) return null; return new Transaction(result.getStrings()); } public interface IBundleInfo { public String getName(); public String[] getPackages(); } public interface ITransaction { public int getLockCount(); public ILock[] getLocks(int start, int end); public IBundleInfo getBundleInfo(ILock lock); public void close(); } private class Transaction implements ITransaction { public Transaction(ArrayList<String> arrayList) { LinkedList<String> stack = new LinkedList<String>(arrayList); transactionID = stack.removeFirst(); count = Integer.parseInt(stack.removeFirst()); stack.addLast(Integer.toString(0)); // putting 0 locks for the moment. stats = new Statistics(stack); } String transactionID; int count; Statistics stats; public String toString() { return "Transaction ID:" + transactionID; } @Override public int getLockCount() { return count; } @Override public ILock[] getLocks(int start, int end) { Message msg = new Message(new ArrayList<String>(Arrays.asList(new String[] {GET_LOCK, transactionID, Integer.toString(start), Integer.toString(end)}))); Message result = connection.request(msg); if (result == null) return null; return stats.unSerializeLocks(new LinkedList<String>(result.getStrings())); } public IBundleInfo getBundleInfo(ILock lock) { String acquisitionRoot = ""; String[] stackTrace = lock.getStackTrace(); if (stackTrace.length > 0) { acquisitionRoot = stackTrace[0]; int index = acquisitionRoot.indexOf("("); if (index != -1) acquisitionRoot = acquisitionRoot.substring(0, index); } Message msg = new Message(new ArrayList<String>(Arrays.asList(new String[] {GET_BUNDLE_INFO, transactionID, lock.getID(), acquisitionRoot}))); Message result = connection.request(msg); if (result == null) return null; return stats.unSerializeBundleInfo(new LinkedList<String>(result.getStrings())); } @Override public void close() { Message msg = new Message(new ArrayList<String>(Arrays.asList(new String[] {CLOSE_TRANSACTION, transactionID}))); connection.request(msg); } } private static class ServerConnection { NetworkServer.Session session; public ServerConnection(NetworkServer.Session session) { this.session = session; } public boolean isClosed() { return session.isClosed(); } public void stop() { session.close(); } public Message request(Message msg) { try { return msg.request(session.getOutput(), session.getInput()); } catch (SocketException e) { // socket is closed } catch (IOException e) { e.printStackTrace(); } return null; } } public static class Message { public Message(ArrayList<String> strings) { this.strings = strings; } public Message(String[] strings) { this.strings = new ArrayList<String>(Arrays.asList(strings)); } public Message() { } public ArrayList<String> getStrings() { return strings; } ArrayList<String> strings; public Message request(PrintStream output, DataInputStream input) throws IOException { NetworkUtil.writeString(output, VERSION_ID); NetworkUtil.writeStringArray(output, strings); output.flush(); String result = NetworkUtil.readString(input); if (!result.equals("OK")) throw new IOException("error returned from the server:" + result); Message content = new Message(NetworkUtil.readStringArray(input)); return content; } public void respond(PrintStream output) throws IOException { NetworkUtil.writeStringArray(output, strings); output.flush(); } public void readMessage(DataInputStream input) throws IOException { String version = NetworkUtil.readString(input); if (!version.equals(VERSION_ID)) { throw new IOException("version incompatible: server(" + VERSION_ID + ") , client(" + version + ")"); } strings = NetworkUtil.readStringArray(input); } public void sendError(PrintStream output, String value) throws IOException { NetworkUtil.writeString(output, value); } public void sendOK(PrintStream output) throws IOException { NetworkUtil.writeString(output, "OK"); } } @Override public void handle(Session session) { synchronized(this) { if (connection != null) connection.stop(); connection = new ServerConnection(session); } } @Override public void close() { if (connection != null) connection.stop(); } }