Java tutorial
/* * Copyright 1999-2008 University of Chicago * * 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. */ package org.globus.workspace.network.defaults; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.globus.workspace.ProgrammingError; import org.globus.workspace.Lager; import org.globus.workspace.network.AssociationEntry; import org.globus.workspace.network.Association; import org.globus.workspace.persistence.PersistenceAdapter; import org.globus.workspace.persistence.WorkspaceDatabaseException; import org.nimbustools.api.services.rm.ResourceRequestDeniedException; import org.nimbustools.api.services.rm.ManageException; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import java.util.Enumeration; public class Util { private static final Log logger = LogFactory.getLog(Util.class.getName()); private static final String NOENTRY = "none"; private static final String COMMENT_CHAR = "#"; /** * @param name network name * @param db db * @param vmid for logging * @param eventLog for logging * @return Object[] length 2 * AssociationEntry [0] * String dns setting [1] * @throws ResourceRequestDeniedException denial */ static Object[] getNextEntry(String name, PersistenceAdapter db, int vmid, boolean eventLog) throws ResourceRequestDeniedException { if (db == null) { throw new IllegalArgumentException("null persistence adapter"); } final Hashtable associations; try { associations = db.currentAssociations(); } catch (WorkspaceDatabaseException e) { logger.fatal(e.getMessage(), e); throw new ResourceRequestDeniedException("internal error, db problem"); } final Object[] entryAndDns = nextAssociationEntry(name, associations); final AssociationEntry entry; if (entryAndDns == null || entryAndDns[0] == null) { final String err = "network '" + name + "' is not currently available"; logger.error(err); throw new ResourceRequestDeniedException(err); } else { entry = (AssociationEntry) entryAndDns[0]; logger.debug("entry picked = " + entry); } try { db.replaceAssociationEntry(name, entry); } catch (WorkspaceDatabaseException e) { logger.fatal(e.getMessage(), e); throw new ResourceRequestDeniedException("internal error, db problem"); } if (eventLog) { logger.info(Lager.ev(vmid) + "'" + name + "' network " + "entry leased, ip=" + entry.getIpAddress()); } return entryAndDns; } static void retireEntry(String name, String ipAddress, PersistenceAdapter db, int trackingID) throws ManageException { if (db == null) { throw new IllegalArgumentException("null persistence adapter"); } final Hashtable associations = db.currentAssociations(); final Association assoc = (Association) associations.get(name); if (assoc == null) { logger.error("no network '" + name + "'"); return; } final List entries = assoc.getEntries(); if (entries == null || entries.isEmpty()) { logger.error(Lager.id(trackingID) + " network '" + name + "' has no entries"); return; } // evidence for storing the entry list as an IP keyed hashtable... final Iterator iter = entries.iterator(); AssociationEntry entry = null; boolean found = false; while (!found && iter.hasNext()) { entry = (AssociationEntry) iter.next(); if (entry.getIpAddress().equals(ipAddress.trim())) { found = true; } } if (!found) { throw new ManageException( Lager.id(trackingID) + " entry was " + "not found in '" + name + "': " + ipAddress); } entry.setInUse(false); db.replaceAssociationEntry(name, entry); logger.info(Lager.ev(trackingID) + "'" + name + "' network lease " + "is over, ip=" + entry.getIpAddress()); } private static Object[] nextAssociationEntry(String name, Hashtable associations) throws ResourceRequestDeniedException { final Association assoc = (Association) associations.get(name); if (assoc == null) { final String err = "'" + name + "' is not a valid network name"; logger.error(err); throw new ResourceRequestDeniedException(err); } final List entries = assoc.getEntries(); if (entries == null || entries.isEmpty()) { return null; // *** EARLY RETURN *** } final Iterator iter = assoc.getEntries().iterator(); AssociationEntry entry = null; while (iter.hasNext()) { entry = (AssociationEntry) iter.next(); if (!entry.isInUse()) { entry.setInUse(true); break; } entry = null; } if (entry == null) { return null; // *** EARLY RETURN *** } final Object[] objs = new Object[2]; objs[0] = entry; final String DNS = assoc.getDns(); if (DNS == null || DNS.equalsIgnoreCase("none")) { objs[1] = "null"; } else { objs[1] = DNS; } return objs; } /** * @param associationDir association directory, may not be null * @param previous previous entries * @return updated entries * @throws Exception problem */ static Hashtable loadDirectory(File associationDir, Hashtable previous) throws Exception { if (associationDir == null) { throw new IllegalArgumentException("null associationDir"); } final String[] listing = associationDir.list(); if (listing == null) { // null return from list() is different than zero results, it denotes a real problem throw new Exception( "Problem listing contents of directory '" + associationDir.getAbsolutePath() + '\''); } final Hashtable newAssocSet = new Hashtable(listing.length); for (int i = 0; i < listing.length; i++) { final String path = associationDir.getAbsolutePath() + File.separator + listing[i]; final File associationFile = new File(path); if (!associationFile.isFile()) { logger.warn("not a file: '" + path + "'"); continue; } final String assocName = associationFile.getName(); Association oldassoc = null; if (previous != null) { oldassoc = (Association) previous.get(assocName); // skip reading if file modification time isn't newer than last // container boot if (oldassoc != null) { if (oldassoc.getFileTime() == associationFile.lastModified()) { logger.info("file modification time for network '" + assocName + "' is not newer, using old configuration"); newAssocSet.put(assocName, oldassoc); continue; } } } final Association newassoc = getNewAssoc(assocName, associationFile, oldassoc); if (newassoc != null) { newAssocSet.put(assocName, newassoc); } } if (previous == null || previous.isEmpty()) { return newAssocSet; } // Now look at previous entries in database for entries that were // there and now entirely gone. // If in use, we don't do anything. When retired and the entry is // not in DB, a warning will trip but that is it. From then on, the // address will be gone. final Enumeration en = previous.keys(); while (en.hasMoreElements()) { final String assocname = (String) en.nextElement(); final Association oldassoc = (Association) previous.get(assocname); if (oldassoc == null) { throw new ProgrammingError("all networks " + "in the hashmap should be non-null"); } if (newAssocSet.containsKey(assocname)) { logChangedAssoc(assocname, (Association) newAssocSet.get(assocname), oldassoc); } else { logger.info("Previously configured network '" + assocname + "' is not present in the new configuration. " + goneStatus(oldassoc)); } } return newAssocSet; } // Parses a new association, if one with same name existed before, // this compares the two. // If an entry existed with the same IP address, the entry is reconfigured // entirely from the new file's information. If the entry is currently // in use this is recorded. private static Association getNewAssoc(String assocName, File file, Association oldassoc) throws IOException { final Association assoc = loadOne(file); if (assoc == null) { return null; } if (oldassoc == null) { return assoc; } final List assocEntries = assoc.getEntries(); if (assocEntries == null || assocEntries.isEmpty()) { // no conflicts are possible return assoc; } final List oldassocEntries = oldassoc.getEntries(); if (oldassocEntries == null || oldassocEntries.isEmpty()) { return assoc; } if (assoc.getDns() != null && !assoc.getDns().equals(oldassoc.getDns())) { logger.info( "Network '" + assocName + "': DNS changed from " + oldassoc.getDns() + " to " + assoc.getDns()); } for (Object assocEntry : assocEntries) { final AssociationEntry entry = (AssociationEntry) assocEntry; final AssociationEntry oldentry = getMatchingIpEntry(entry.getIpAddress(), oldassocEntries); if (oldentry == null) { continue; } logDifferences(assocName, entry, oldentry); // Any change is OK. // We know it has same IP address and that is enough to retire // with. But the in-use flag MUST match the old one. entry.setInUse(oldentry.isInUse()); if (!entry.isExplicitMac()) { entry.setMac(oldentry.getMac()); } if (entry.isInUse()) { logger.debug("Network '" + assocName + "', ip " + entry.getIpAddress() + " is currently in use."); } } return assoc; } private static AssociationEntry getMatchingIpEntry(String ip, List entries) { if (ip == null) { throw new IllegalArgumentException("ip is null"); } final Iterator iter = entries.iterator(); while (iter.hasNext()) { final AssociationEntry entry = (AssociationEntry) iter.next(); if (ip.equals(entry.getIpAddress())) { return entry; } } return null; } private static void logDifferences(String assocName, AssociationEntry entry, AssociationEntry oldentry) { boolean same = true; final StringBuffer buf = new StringBuffer("Network '"); buf.append(assocName).append("', ip ").append(entry.getIpAddress()) .append(": has differences in new configuration. "); if (diffErator(buf, "hostname", entry.getHostname(), oldentry.getHostname())) { same = false; } if (diffErator(buf, "gateway", entry.getGateway(), oldentry.getGateway())) { same = false; buf.append("; "); } if (diffErator(buf, "netmask", entry.getSubnetMask(), oldentry.getSubnetMask())) { same = false; buf.append("; "); } if (diffErator(buf, "broadcast", entry.getBroadcast(), oldentry.getBroadcast())) { same = false; buf.append("; "); } if (entry.isExplicitMac() && diffErator(buf, "MAC", entry.getMac(), oldentry.getMac())) { same = false; buf.append("; "); } if (!same) { logger.info(buf.toString()); } } // adds string explanation of any difference, returns true if different private static boolean diffErator(StringBuffer buf, String fieldname, String newval, String oldval) { if (oldval == null && newval == null) { return false; } if (oldval != null && newval != null) { if (oldval.equals(newval)) { return false; } } String oldstr = "none"; String newstr = "none"; if (oldval != null) { oldstr = "'" + oldval + "'"; } if (newval != null) { newstr = "'" + newval + "'"; } buf.append(fieldname).append(" ").append(oldstr).append("-->").append(newstr).append("; "); return true; } private static Association loadOne(File file) throws IOException { Association association = null; final List associationList = new LinkedList(); // Read the contents of file InputStream in = null; InputStreamReader isr = null; BufferedReader bufrd = null; String line; try { in = new FileInputStream(file); isr = new InputStreamReader(in); bufrd = new BufferedReader(isr); line = bufrd.readLine(); if (line != null) { // find first non-comment, non-empty line boolean notfound = true; while (notfound) { try { association = parseDNS(line); notfound = false; } catch (Exception e) { line = bufrd.readLine(); if (line == null) { association = null; break; } } } if (association == null) { logger.error("DNS information incorrectly" + " specified, skipping entire network. " + "Path: " + file.getAbsolutePath()); return null; } } else { logger.warn("network file '" + file.getAbsolutePath() + "' is empty, skipping"); return null; } // the rest while ((line = bufrd.readLine()) != null) { line = line.trim(); if (line.length() > 0) { AssociationEntry entry = parseAssoc(line); if (entry != null) { associationList.add(entry); } } // can have an association with no entries } } finally { try { if (bufrd != null) { bufrd.close(); } if (isr != null) { isr.close(); } if (in != null) { in.close(); } } catch (Exception e) { logger.error("", e); } } association.setEntries(associationList); association.setFileTime(file.lastModified()); return association; } private static Association parseDNS(String line) throws CommentException, BlankLineException { if (line == null) { return null; } final StringTokenizer st = new StringTokenizer(line); if (st.countTokens() == 0) { throw new BlankLineException(); } final String dns = st.nextToken().trim(); if (dns.startsWith(COMMENT_CHAR)) { throw new CommentException(); } if (dns.equals(NOENTRY)) { return new Association(null); } else { return new Association(dns); } } private static AssociationEntry parseAssoc(String line) { if (line == null) { return null; } final StringTokenizer st = new StringTokenizer(line); // don't note blank, cosmetic lines if (st.countTokens() == 0) { return null; } // ignore comments final String hostname = st.nextToken().trim(); if (hostname.startsWith(COMMENT_CHAR)) { return null; } final int tokens = st.countTokens(); if (tokens != 4 && tokens != 5) { logger.error("entry in network file is invalid. Expecting either" + " five or six tokens (MAC optional)" + " -- line = '" + line + "'"); return null; } if (hostname.equals(NOENTRY)) { logger.error("network entry must contain hostname" + " in first position -- line = '" + line + "'"); return null; } final String ipaddress = st.nextToken().trim(); if (ipaddress.equals(NOENTRY)) { logger.error("network entry must contain IP" + " address in second position -- line = '" + line + "'"); return null; } String gateway = st.nextToken().trim(); if (gateway.equals(NOENTRY)) { // perhaps they do not need other network access gateway = null; } String broadcast = st.nextToken().trim(); if (broadcast.equals(NOENTRY)) { broadcast = null; } String subnetmask = st.nextToken().trim(); if (subnetmask.equals(NOENTRY)) { subnetmask = null; } // mac can be optionally supplied as final token String mac = null; if (tokens == 5) { mac = st.nextToken().trim(); if (mac.equals(NOENTRY)) { mac = null; } else { if (!MacUtil.isValidMac(mac, false)) { logger.error("Invalid MAC address entry -- line = '" + line + "'."); return null; } } } final AssociationEntry entry = new AssociationEntry(ipaddress, mac, hostname, gateway, broadcast, subnetmask); entry.setExplicitMac(mac != null); return entry; } private static void logChangedAssoc(String assocname, Association newassoc, Association oldassoc) { final List oldentries = oldassoc.getEntries(); final List newentries = newassoc.getEntries(); final Iterator oldEntriesIter = oldentries.iterator(); while (oldEntriesIter.hasNext()) { final AssociationEntry oldentry = (AssociationEntry) oldEntriesIter.next(); boolean foundOldEntry = false; final Iterator newEntriesIter = newentries.iterator(); while (newEntriesIter.hasNext()) { final AssociationEntry newentry = (AssociationEntry) newEntriesIter.next(); if (oldentry.getIpAddress().equals(newentry.getIpAddress())) { foundOldEntry = true; break; } } if (!foundOldEntry) { String inuse = ""; if (oldentry.isInUse()) { inuse = " Note it is currently in use."; } logger.info("IP '" + oldentry.getIpAddress() + "' is not present in the new " + "configuration for network '" + assocname + "'. Deleted from available " + "addresses in address pool." + inuse); } } } // the entire old pool was removed, log what we can private static String goneStatus(Association oldassoc) { final List oldentries = oldassoc.getEntries(); if (oldentries == null || oldentries.isEmpty()) { return "There were no addresses in that network."; } final StringBuffer buf = new StringBuffer("Contents: "); final Iterator oldEntriesIter = oldentries.iterator(); while (oldEntriesIter.hasNext()) { final AssociationEntry entry = (AssociationEntry) oldEntriesIter.next(); buf.append("ip '").append(entry.getIpAddress()).append("' host '").append(entry.getHostname()) .append("' inuse? ").append(entry.isInUse()).append("; "); } return buf.toString(); } private static class CommentException extends Exception { } private static class BlankLineException extends Exception { } }