001 package com.github.sarxos.webcam;
002
003 import java.io.DataInputStream;
004 import java.io.DataOutputStream;
005 import java.io.File;
006 import java.io.FileInputStream;
007 import java.io.FileOutputStream;
008 import java.io.IOException;
009 import java.util.concurrent.atomic.AtomicBoolean;
010
011 import org.slf4j.Logger;
012 import org.slf4j.LoggerFactory;
013
014
015 /**
016 * This class is used as a global (system) lock preventing other processes from
017 * using the same camera while it's open.
018 *
019 * @author Bartosz Firyn (sarxos)
020 */
021 public class WebcamLock {
022
023 /**
024 * Logger.
025 */
026 private static final Logger LOG = LoggerFactory.getLogger(WebcamLock.class);
027
028 /**
029 * Update interval (ms).
030 */
031 private static final long INTERVAL = 2000;
032
033 /**
034 * Used to update lock state.
035 *
036 * @author sarxos
037 */
038 private class LockUpdater extends Thread {
039
040 public LockUpdater() {
041 super();
042 setName(String.format("webcam-lock-[%s]", webcam.getName()));
043 setDaemon(true);
044 setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
045 }
046
047 @Override
048 public void run() {
049 do {
050 update();
051 try {
052 Thread.sleep(INTERVAL);
053 } catch (InterruptedException e) {
054 LOG.debug("Lock updater has been interrupted");
055 return;
056 }
057 } while (locked.get());
058 }
059
060 }
061
062 /**
063 * And the Webcam we will be locking.
064 */
065 private final Webcam webcam;
066
067 private Thread updater = null;
068
069 private AtomicBoolean locked = new AtomicBoolean(false);
070
071 private File lock = null;
072
073 /**
074 * Creates global webcam lock.
075 *
076 * @param webcam the webcam instance to be locked
077 */
078 protected WebcamLock(Webcam webcam) {
079 super();
080 this.webcam = webcam;
081 this.lock = new File(System.getProperty("java.io.tmpdir"), getLockName());
082 }
083
084 private String getLockName() {
085 return String.format(".webcam-lock-%d", Math.abs(webcam.getName().hashCode()));
086 }
087
088 private void write(long value) {
089
090 String name = getLockName();
091
092 File tmp = null;
093 DataOutputStream dos = null;
094
095 try {
096 tmp = File.createTempFile(name, "");
097
098 dos = new DataOutputStream(new FileOutputStream(tmp));
099 dos.writeLong(value);
100 dos.flush();
101
102 } catch (IOException e) {
103 throw new WebcamException(e);
104 } finally {
105 if (dos != null) {
106 try {
107 dos.close();
108 } catch (IOException e) {
109 throw new WebcamException(e);
110 }
111 }
112 }
113
114 if (!locked.get()) {
115 return;
116 }
117
118 if (!tmp.renameTo(lock)) {
119 LOG.warn("Ooops, system was not able to rename lock file from {} to {}", tmp, lock);
120 }
121 }
122
123 private long read() {
124 DataInputStream dis = null;
125 try {
126 return (dis = new DataInputStream(new FileInputStream(lock))).readLong();
127 } catch (IOException e) {
128 throw new WebcamException(e);
129 } finally {
130 if (dis != null) {
131 try {
132 dis.close();
133 } catch (IOException e) {
134 throw new WebcamException(e);
135 }
136 }
137 }
138 }
139
140 private void update() {
141 write(System.currentTimeMillis());
142 }
143
144 /**
145 * Lock webcam.
146 */
147 public void lock() {
148
149 if (isLocked()) {
150 throw new WebcamLockException(String.format("Webcam %s has already been locked", webcam.getName()));
151 }
152
153 if (!locked.compareAndSet(false, true)) {
154 return;
155 }
156
157 LOG.debug("Lock {}", webcam);
158
159 update();
160
161 updater = new LockUpdater();
162 updater.start();
163 }
164
165 /**
166 * Unlock webcam.
167 */
168 public void unlock() {
169
170 if (!locked.compareAndSet(true, false)) {
171 return;
172 }
173
174 LOG.debug("Unlock {}", webcam);
175
176 updater.interrupt();
177
178 write(-1);
179
180 if (!lock.delete()) {
181 lock.deleteOnExit();
182 }
183 }
184
185 /**
186 * Check if webcam is locked.
187 *
188 * @return True if webcam is locked, false otherwise
189 */
190 public boolean isLocked() {
191
192 // check if locked by current process
193
194 if (locked.get()) {
195 return true;
196 }
197
198 // check if locked by other process
199
200 if (!lock.exists()) {
201 return false;
202 }
203
204 long now = System.currentTimeMillis();
205 long tsp = read();
206
207 LOG.trace("Lock timestamp {} now {} for {}", tsp, now, webcam);
208
209 if (tsp > now - INTERVAL * 2) {
210 return true;
211 }
212
213 return false;
214 }
215 }