Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgRepositoryLock.java @ 487:db48a77ec8ff
Access to reposiotry lock mechanism via HgRepositoryLock
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 13 Aug 2012 18:11:47 +0200 |
parents | src/org/tmatesoft/hg/internal/Lock.java@d740edfff563 |
children | 45b3b6ca046f |
comparison
equal
deleted
inserted
replaced
486:d740edfff563 | 487:db48a77ec8ff |
---|---|
1 /* | |
2 * Copyright (c) 2012 TMate Software Ltd | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; version 2 of the License. | |
7 * | |
8 * This program is distributed in the hope that it will be useful, | |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 * GNU General Public License for more details. | |
12 * | |
13 * For information on how to redistribute this software under | |
14 * the terms of a license other than GNU General Public License | |
15 * contact TMate Software at support@hg4j.com | |
16 */ | |
17 package org.tmatesoft.hg.repo; | |
18 | |
19 import java.io.File; | |
20 import java.io.FileInputStream; | |
21 import java.io.FileOutputStream; | |
22 import java.io.IOException; | |
23 import java.lang.management.ManagementFactory; | |
24 import java.net.InetAddress; | |
25 import java.nio.ByteBuffer; | |
26 import java.nio.channels.FileChannel; | |
27 | |
28 import org.tmatesoft.hg.internal.Internals; | |
29 | |
30 /** | |
31 * NOT SAFE FOR MULTITHREAD USE! | |
32 * | |
33 * Unlike original mechanism, we don't use symlinks, rather files, as it's easier to implement | |
34 * | |
35 * @author Artem Tikhomirov | |
36 * @author TMate Software Ltd. | |
37 */ | |
38 public class HgRepositoryLock { | |
39 /* | |
40 * Lock .hg/ except .hg/store/ .hg/wlock (HgRepository.repoPathHelper("wlock")) | |
41 * Lock .hg/store/ .hg/store/lock (HgRepository.repoPathHelper("store/lock") ???) | |
42 */ | |
43 | |
44 private final File lockFile; | |
45 private int use = 0; | |
46 private final int timeoutSeconds; | |
47 | |
48 HgRepositoryLock(File lock, int timeoutInSeconds) { | |
49 lockFile = lock; | |
50 timeoutSeconds = timeoutInSeconds; | |
51 } | |
52 | |
53 /** | |
54 * Tries to read lock file and supplies hostname:pid (or just pid) information from there | |
55 * @return <code>null</code> if no lock file available at the moment | |
56 */ | |
57 public String readLockInfo() { | |
58 if (lockFile.exists()) { | |
59 try { | |
60 byte[] bytes = read(lockFile); | |
61 if (bytes != null && bytes.length > 0) { | |
62 return new String(bytes); | |
63 } | |
64 } catch (Exception ex) { | |
65 // deliberately ignored | |
66 } | |
67 } | |
68 return null; | |
69 } | |
70 | |
71 public boolean isLocked() { | |
72 return use > 0; | |
73 } | |
74 | |
75 public void acquire() { | |
76 if (use > 0) { | |
77 use++; | |
78 return; | |
79 } | |
80 StringBuilder lockDescription = new StringBuilder(); | |
81 lockDescription.append(getHostname()); | |
82 lockDescription.append(':'); | |
83 lockDescription.append(getPid()); | |
84 byte[] bytes = lockDescription.toString().getBytes(); | |
85 long stopTime = timeoutSeconds < 0 ? -1 : (System.currentTimeMillis() + timeoutSeconds*1000); | |
86 do { | |
87 synchronized(this) { | |
88 try { | |
89 if (!lockFile.exists()) { | |
90 write(lockFile, bytes); | |
91 use++; | |
92 return; | |
93 } | |
94 } catch (IOException ex) { | |
95 // deliberately ignored | |
96 } | |
97 try { | |
98 wait(1000); | |
99 } catch (InterruptedException ex) { | |
100 // deliberately ignored | |
101 } | |
102 } | |
103 | |
104 } while (stopTime == -1/*no timeout*/ || System.currentTimeMillis() <= stopTime); | |
105 String msg = String.format("Failed to aquire lock, waited for %d seconds, present owner: '%s'", timeoutSeconds, readLockInfo()); | |
106 throw new HgInvalidStateException(msg); | |
107 } | |
108 | |
109 public void release() { | |
110 if (use == 0) { | |
111 throw new HgInvalidStateException(""); | |
112 } | |
113 use--; | |
114 if (use > 0) { | |
115 return; | |
116 } | |
117 // do release | |
118 lockFile.delete(); | |
119 } | |
120 | |
121 protected String getHostname() { | |
122 try { | |
123 return InetAddress.getLocalHost().getHostName(); | |
124 } catch (Exception ex) { | |
125 return "localhost"; | |
126 } | |
127 } | |
128 | |
129 protected int getPid() { | |
130 try { | |
131 // @see http://blog.igorminar.com/2007/03/how-java-application-can-discover-its.html | |
132 if (!Internals.runningOnWindows()) { | |
133 File f = new File("/proc/self"); | |
134 if (f.exists()) { | |
135 // /proc/self is a symlink to /proc/pid/ directory | |
136 return Integer.parseInt(f.getCanonicalFile().getName()); | |
137 } | |
138 } | |
139 String rtBean = ManagementFactory.getRuntimeMXBean().getName(); | |
140 int x; | |
141 if ((x = rtBean.indexOf('@')) != -1) { | |
142 return Integer.parseInt(rtBean.substring(0, x)); | |
143 } | |
144 return -1; | |
145 } catch (Exception ex) { | |
146 return -1; | |
147 } | |
148 } | |
149 | |
150 private static void write(File f, byte[] content) throws IOException { | |
151 FileOutputStream fos = new FileOutputStream(f); | |
152 fos.write(content); | |
153 fos.close(); | |
154 } | |
155 | |
156 private static byte[] read(File f) throws IOException { | |
157 FileChannel fc = new FileInputStream(f).getChannel(); | |
158 ByteBuffer bb = ByteBuffer.allocate(Internals.ltoi(fc.size())); | |
159 fc.read(bb); | |
160 fc.close(); | |
161 return bb.array(); | |
162 } | |
163 } |