PortFile.java revision 2981:d1e5707cd4eb
1/* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.sjavac.server; 27 28import java.io.File; 29import java.io.FileNotFoundException; 30import java.io.IOException; 31import java.io.RandomAccessFile; 32import java.nio.channels.ClosedChannelException; 33import java.nio.channels.FileChannel; 34import java.nio.channels.FileLock; 35import java.nio.channels.FileLockInterruptionException; 36import java.util.concurrent.Semaphore; 37 38import com.sun.tools.javac.util.Assert; 39import com.sun.tools.sjavac.Log; 40 41/** 42 * The PortFile class mediates access to a short binary file containing the tcp/ip port (for the localhost) 43 * and a cookie necessary for the server answering on that port. The file can be locked using file system 44 * primitives to avoid race conditions when several javac clients are started at the same. Note that file 45 * system locking is not always supported on a all operating systems and/or file systems. 46 * 47 * <p><b>This is NOT part of any supported API. 48 * If you write code that depends on this, you do so at your own risk. 49 * This code and its internal interfaces are subject to change or 50 * deletion without notice.</b> 51 */ 52public class PortFile { 53 54 // Port file format: 55 // byte ordering: high byte first = big endian 56 // Magic nr, 4 byte int, first in file. 57 private final static int magicNr = 0x1174; 58 // Followed by a 4 byte int, with the port nr. 59 // Followed by a 8 byte long, with cookie nr. 60 61 private String filename; 62 private File file; 63 private File stopFile; 64 private RandomAccessFile rwfile; 65 private FileChannel channel; 66 67 // FileLock used to solve inter JVM synchronization, lockSem used to avoid 68 // JVM internal OverlappingFileLockExceptions. 69 // Class invariant: lock.isValid() <-> lockSem.availablePermits() == 0 70 private FileLock lock; 71 private Semaphore lockSem = new Semaphore(1); 72 73 private boolean containsPortInfo; 74 private int serverPort; 75 private long serverCookie; 76 private int myServerPort; 77 private long myServerCookie; 78 79 /** 80 * Create a new portfile. 81 * @param fn is the path to the file. 82 */ 83 public PortFile(String fn) throws FileNotFoundException { 84 filename = fn; 85 file = new File(filename); 86 stopFile = new File(filename+".stop"); 87 rwfile = new RandomAccessFile(file, "rw"); 88 // The rwfile should only be readable by the owner of the process 89 // and no other! How do we do that on a RandomAccessFile? 90 channel = rwfile.getChannel(); 91 containsPortInfo = false; 92 lock = null; 93 } 94 95 /** 96 * Lock the port file. 97 */ 98 public void lock() throws IOException, InterruptedException { 99 lockSem.acquire(); 100 lock = channel.lock(); 101 } 102 103 /** 104 * Read the values from the port file in the file system. 105 * Expects the port file to be locked. 106 */ 107 public void getValues() { 108 containsPortInfo = false; 109 if (lock == null) { 110 // Not locked, remain ignorant about port file contents. 111 return; 112 } 113 try { 114 if (rwfile.length()>0) { 115 rwfile.seek(0); 116 int nr = rwfile.readInt(); 117 serverPort = rwfile.readInt(); 118 serverCookie = rwfile.readLong(); 119 120 if (nr == magicNr) { 121 containsPortInfo = true; 122 } else { 123 containsPortInfo = false; 124 } 125 } 126 } catch (IOException e) { 127 containsPortInfo = false; 128 } 129 } 130 131 /** 132 * Did the locking and getValues succeed? 133 */ 134 public boolean containsPortInfo() { 135 return containsPortInfo; 136 } 137 138 /** 139 * If so, then we can acquire the tcp/ip port on localhost. 140 */ 141 public int getPort() { 142 Assert.check(containsPortInfo); 143 return serverPort; 144 } 145 146 /** 147 * If so, then we can acquire the server cookie. 148 */ 149 public long getCookie() { 150 Assert.check(containsPortInfo); 151 return serverCookie; 152 } 153 154 /** 155 * Store the values into the locked port file. 156 */ 157 public void setValues(int port, long cookie) throws IOException { 158 Assert.check(lock != null); 159 rwfile.seek(0); 160 // Write the magic nr that identifes a port file. 161 rwfile.writeInt(magicNr); 162 rwfile.writeInt(port); 163 rwfile.writeLong(cookie); 164 myServerPort = port; 165 myServerCookie = cookie; 166 } 167 168 /** 169 * Delete the port file. 170 */ 171 public void delete() throws IOException { 172 // Access to file must be closed before deleting. 173 rwfile.close(); 174 // Now delete. 175 file.delete(); 176 } 177 178 /** 179 * Is the port file still there? 180 */ 181 public boolean exists() throws IOException { 182 return file.exists(); 183 } 184 185 /** 186 * Is a stop file there? 187 */ 188 public boolean markedForStop() throws IOException { 189 if (stopFile.exists()) { 190 try { 191 stopFile.delete(); 192 } catch (Exception e) 193 {} 194 return true; 195 } 196 return false; 197 } 198 199 /** 200 * Unlock the port file. 201 */ 202 public void unlock() throws IOException { 203 Assert.check(lock != null); 204 lock.release(); 205 lock = null; 206 lockSem.release(); 207 } 208 209 /** 210 * Wait for the port file to contain values that look valid. 211 */ 212 public void waitForValidValues() throws IOException, InterruptedException { 213 final int MAX_ATTEMPTS = 10; 214 final int MS_BETWEEN_ATTEMPTS = 500; 215 long startTime = System.currentTimeMillis(); 216 for (int attempt = 0; ; attempt++) { 217 Log.debug("Looking for valid port file values..."); 218 lock(); 219 getValues(); 220 unlock(); 221 if (containsPortInfo) { 222 Log.debug("Valid port file values found after " + (System.currentTimeMillis() - startTime) + " ms"); 223 return; 224 } 225 if (attempt >= MAX_ATTEMPTS) { 226 throw new IOException("No port file values materialized. Giving up after " + 227 (System.currentTimeMillis() - startTime) + " ms"); 228 } 229 Thread.sleep(MS_BETWEEN_ATTEMPTS); 230 } 231 } 232 233 /** 234 * Check if the portfile still contains my values, assuming that I am the server. 235 */ 236 public boolean stillMyValues() throws IOException, FileNotFoundException, InterruptedException { 237 for (;;) { 238 try { 239 lock(); 240 getValues(); 241 unlock(); 242 if (containsPortInfo) { 243 if (serverPort == myServerPort && 244 serverCookie == myServerCookie) { 245 // Everything is ok. 246 return true; 247 } 248 // Someone has overwritten the port file. 249 // Probably another javac server, lets quit. 250 return false; 251 } 252 // Something else is wrong with the portfile. Lets quit. 253 return false; 254 } catch (FileLockInterruptionException e) { 255 continue; 256 } 257 catch (ClosedChannelException e) { 258 // The channel has been closed since sjavac is exiting. 259 return false; 260 } 261 } 262 } 263 264 /** 265 * Return the name of the port file. 266 */ 267 public String getFilename() { 268 return filename; 269 } 270} 271