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.globus.workspace.network.Association; import org.globus.workspace.network.AssociationEntry; import org.nimbustools.api.services.rm.ResourceRequestDeniedException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; public class MacUtil { private static final Log logger = LogFactory.getLog(MacUtil.class.getName()); public static final String VALID_MAC_CHARACTERS = "0123456789ABCDEF"; public static final char[] MAC_ARRAY = VALID_MAC_CHARACTERS.toCharArray(); private static final Object list_lock = new Object(); private static final Random random = new Random(); public static List<String> handleMacs(Map<String, Association> previous, Map<String, Association> current, String macPrefix) throws ResourceRequestDeniedException { if (previous == null) { throw new IllegalArgumentException("previous may not be null"); } if (current == null) { throw new IllegalArgumentException("current may not be null"); } if (macPrefix == null) { throw new IllegalArgumentException("macPrefix may not be null"); } final Map<String, AssociationEntry> inUse = getInUseMacMap(previous); final Set<String> explicit = new HashSet<String>(); for (Map.Entry<String, Association> assocPair : current.entrySet()) { final String assocName = assocPair.getKey(); final Association assoc = assocPair.getValue(); if (assoc == null || assoc.getEntries() == null) { continue; } for (Object entryObject : assoc.getEntries()) { final AssociationEntry entry = (AssociationEntry) entryObject; if (entry.isExplicitMac()) { final AssociationEntry inUseEntry = inUse.get(entry.getMac()); if (inUseEntry != null) { final String inUseIp = inUseEntry.getIpAddress(); if (inUseIp == null || !inUseIp.equals(entry.getIpAddress())) { logger.error("An explicit MAC in network '" + assocName + "' collides with an in-use network entry! " + "Explicit: " + entry.toString() + "\nIn use: " + inUseEntry.toString()); entry.setMac(null); entry.setExplicitMac(false); } } } if (entry.isExplicitMac()) { if (!explicit.add(entry.getMac())) { logger.warn("Duplicate explicit MAC? " + entry.getMac()); } } } } final List<String> macList = new ArrayList<String>(); macList.addAll(inUse.keySet()); macList.addAll(explicit); for (Association assoc : current.values()) { for (Object entryObject : assoc.getEntries()) { final AssociationEntry entry = (AssociationEntry) entryObject; _setMac(entry, macPrefix, macList, explicit); } } return macList; } private static Map<String, AssociationEntry> getInUseMacMap(Map<String, Association> previous) { Map<String, AssociationEntry> inUse = new HashMap<String, AssociationEntry>(); for (Association assoc : previous.values()) { for (Object entryObject : assoc.getEntries()) { final AssociationEntry entry = (AssociationEntry) entryObject; if (entry.isInUse()) { final String mac = entry.getMac(); if (mac == null || mac.length() == 0) { logger.warn("Network association entry is supposedly " + "in use but has no MAC address..? " + entry.toString()); } final AssociationEntry clobbered = inUse.put(mac, entry); if (clobbered != null) { logger.warn("There appear to be duplicate MAC addresses in use: " + clobbered.toString() + "\nand\n" + entry.toString()); } } } } return inUse; } /* public static void setMacs(Hashtable new_associations, String macPrefix, List macs) throws ResourceRequestDeniedException { if (macPrefix == null) { throw new IllegalArgumentException("macPrefix may not be null"); } if (new_associations == null) { return; // *** EARLY RETURN *** } final Enumeration els = new_associations.elements(); while (els.hasMoreElements()) { final Association assoc = (Association) els.nextElement(); final List entries = assoc.getEntries(); for (Object entry : entries) { _setMac((AssociationEntry) entry, macPrefix, macs); } } } */ private static void _setMac(AssociationEntry entry, String macPrefix, List<String> macs, Set<String> explicit) throws ResourceRequestDeniedException { if (entry == null) { return; // *** EARLY RETURN *** } final String entryId = "[entry with ip " + entry.getIpAddress() + "]"; final String previousMac = entry.getMac(); boolean prefixReplacement = false; boolean explicitReplacement = false; if (previousMac != null) { if (entry.isExplicitMac()) { return; // *** EARLY RETURN *** } if (explicit.contains(previousMac)) { explicitReplacement = true; } else if (previousMac.startsWith(macPrefix)) { return; // *** EARLY RETURN *** } else { prefixReplacement = true; } } final String newMac = pickNew(macs, macPrefix); entry.setMac(newMac); if (prefixReplacement) { logger.debug("Replaced previous MAC '" + previousMac + "' with " + "a MAC that has the new prefix: '" + newMac + "' " + entryId); } else if (explicitReplacement) { logger.warn("Replaced previous MAC '" + previousMac + "' with '" + newMac + "' because it collided with an explicitly specified MAC. " + entryId); } } static String pickNew(List macs, String macPrefix) throws ResourceRequestDeniedException { final int needed; final int len = macPrefix.length(); switch (len) { case 0: throw new ResourceRequestDeniedException("prefix length 0?"); case 1: needed = 11; break; case 2: needed = 10; break; case 3: needed = 10; break; case 4: needed = 9; break; case 5: needed = 8; break; case 6: needed = 8; break; case 7: needed = 7; break; case 8: needed = 6; break; case 9: needed = 6; break; case 10: needed = 5; break; case 11: needed = 4; break; case 12: needed = 4; break; case 13: needed = 3; break; case 14: needed = 2; break; case 15: needed = 2; break; case 16: needed = 1; break; case 17: needed = 0; break; default: throw new ResourceRequestDeniedException("prefix length >17?"); } if (needed == 0) { logger.warn("prefix is full MAC address, conflict detection OFF"); return macPrefix; } synchronized (list_lock) { final String result; if (needed < 4) { // slow, but tries every single permutation result = _pickNewSerially(macs, macPrefix, needed); } else { // Faster. Does not get at every single permutation but is very // unlikely to fail when address range is 16^4 or higher result = _pickNewWithRandomComponent(macs, macPrefix, needed); } if (result == null) { throw new ResourceRequestDeniedException("no unique MAC address is available"); } else { macs.add(result); return result; } } } // slow, but tries every single permutation private static String _pickNewSerially(List macs, String prefix, int needed) { if (needed == 1) { for (int i = 0; i < MAC_ARRAY.length; i++) { final String attempt = prefix + MAC_ARRAY[i]; if (uniqueMacTest(macs, attempt)) { return attempt; } } return null; } final int L = prefix.length(); if (L == 2 || L == 5 || L == 8 || L == 11 || L == 14) { prefix += ":"; } for (int i = 0; i < MAC_ARRAY.length; i++) { final String answer = _pickNewSerially(macs, prefix + MAC_ARRAY[i], needed - 1); if (answer != null) { return answer; } } return null; } private static boolean uniqueMacTest(List macs, String attempt) { for (Object mac1 : macs) { final String mac = (String) mac1; if (attempt.equals(mac)) { return false; } } return true; } // This is much faster than _pickNewSerially(), especially as the number // of uniques stored in the "macs" List increases into the 100s/1000s. // This will not get at every variation but in practice when a prefix is // short enough (more digits needed), this does not matter (unless setup // actually requires many many thousands of uniques at one time). private static String _pickNewWithRandomComponent(List macs, String macPrefix, long needed) throws ResourceRequestDeniedException { final long limit = (long) StrictMath.pow(16, needed); long count = 0; while (true) { if (count >= limit) { // Even at lowest allowable limit (~65000), this is very // unlikely to ever occur. throw new ResourceRequestDeniedException("Search limit reached (" + count + ") looking for MAC to assign. Please inform developers."); } String attempt = appendRandomCharacter(macPrefix); while (attempt.length() < 17) { attempt = appendRandomCharacter(attempt); } if (uniqueMacTest(macs, attempt)) { return attempt; } else { count += 1; } } } private static String appendRandomCharacter(String sofar) { final int L = sofar.length(); String newString = sofar; if (L == 2 || L == 5 || L == 8 || L == 11 || L == 14) { newString += ":"; } newString += randomChar(); return newString; } private static char randomChar() { return MAC_ARRAY[random.nextInt(MAC_ARRAY.length)]; } public static void main(String[] args) throws Exception { final long mstart = System.currentTimeMillis(); final List macs = new LinkedList(); for (int i = 0; i < 4096; i++) { final String result = pickNew(macs, "AB:CD:12:34:"); if (result == null) { System.out.println("RAN OUT"); break; } else { System.out.println(result); } } final long mstop = System.currentTimeMillis(); System.out.println("ELAPSED: " + Long.toString(mstop - mstart) + "ms"); } public static boolean isValidMac(String mac, boolean prefixOk) { if (mac == null) { throw new IllegalArgumentException("mac may not be null"); } if (!prefixOk && mac.length() != 17) { throw new IllegalArgumentException("MAC must be 17 characters long"); } else if (mac.length() > 17) { throw new IllegalArgumentException("MAC length cannot be more than 17 characters"); } mac = mac.toUpperCase(); final char[] macChars = mac.toCharArray(); for (int i = 0; i < macChars.length; i++) { boolean thisOneOK = false; boolean expectedSeparator = false; if (i == 2 || i == 5 || i == 8 || i == 11 || i == 14) { if (':' == macChars[i]) { thisOneOK = true; } expectedSeparator = true; } else { for (int j = 0; j < MAC_ARRAY.length; j++) { if (MAC_ARRAY[j] == macChars[i]) { thisOneOK = true; break; } } } if (!thisOneOK) { final String tail; if (expectedSeparator) { tail = " (expected separator ':')"; } else { tail = " (expected hex character)"; } logger.warn("Invalid character in MAC (" + mac + "): '" + macChars[i] + "'" + tail); return false; } } return true; } }