/*
* Copyright 2004-2009 the original author or authors.
*
* 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.compass.gps.device;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.gps.ActiveMirrorGpsDevice;
import org.compass.gps.CompassGpsDevice;
import org.compass.gps.CompassGpsException;
/**
* For {@link org.compass.gps.ActiveMirrorGpsDevice}s, the schedule mirror
* device can call the
* {@link org.compass.gps.ActiveMirrorGpsDevice#performMirroring()} in a
* scheduled manner by wrapping the actual
* {@link org.compass.gps.ActiveMirrorGpsDevice}.
* <p/>
* The schedule mirror gps device implements the
* {@link org.compass.gps.ActiveMirrorGpsDevice} interface, and provides access
* to the wrapped gps device using the {@link #getWrappedGpsDevice()}.
* <p/>
* For scheduling, the schedule mirror device uses the
* <code>java.util.Timer</code>, and provides controll over the period,
* daemon, and fixed rate parameters.
*
* @author kimchy
*/
public class ScheduledMirrorGpsDevice extends AbstractMirrorGpsDeviceWrapper implements ActiveMirrorGpsDevice {
private static Log log = LogFactory.getLog(ScheduledMirrorGpsDevice.class);
private ActiveMirrorGpsDevice gpsDevice;
private MirrorGpsDeviceThread thread;
private boolean daemon = true;
private long period = 10000;
/**
* Creates a new instance. Note that the wrapped gps device must be set by
* calling the <code>setGpsDevice</code> and it must implement the .
*/
public ScheduledMirrorGpsDevice() {
}
/**
* Creates a new instance of the scheduled device with the wrapped
* {@link ActiveMirrorGpsDevice} initialized.
*
* @param gpsDevice
*/
public ScheduledMirrorGpsDevice(ActiveMirrorGpsDevice gpsDevice) {
this.gpsDevice = gpsDevice;
setGpsDevice(gpsDevice);
}
/**
* Checks that when setting the wrapped gps device, it is of type
* {@link ActiveMirrorGpsDevice}
*/
public void setGpsDevice(CompassGpsDevice gpsDevice) {
if (!(gpsDevice instanceof ActiveMirrorGpsDevice)) {
throw new IllegalArgumentException("The device must implement the ActiveMirrorGpsDevice interface");
}
this.gpsDevice = (ActiveMirrorGpsDevice) gpsDevice;
super.setGpsDevice(gpsDevice);
}
/**
* Starts the scheduled timer.
*/
public synchronized void start() throws CompassGpsException {
if (isRunning()) {
throw new IllegalStateException("{" + getName() + "} Scheduled mirror device is already running");
}
if (log.isInfoEnabled()) {
log.info("{" + getName() + "} Starting scheduled mirror device with period [" + period + "ms] daemon [" + daemon + "]");
}
this.gpsDevice.start();
thread = new MirrorGpsDeviceThread(period);
thread.setName("Compass Mirror Gps Device [" + getName() + "]");
thread.start();
}
/**
* Stops the scheduled timer.
*/
public synchronized void stop() throws CompassGpsException {
if (!isRunning()) {
throw new IllegalStateException("{" + getName() + "} Scheduled mirror device is already running");
}
if (log.isInfoEnabled()) {
log.info("{" + getName() + "} Stopping scheduled mirror device");
}
thread.cancel();
thread = null;
this.gpsDevice.stop();
}
/**
* Returns the wrapped active mirror gps device.
*/
public ActiveMirrorGpsDevice getWrappedGpsDevice() {
return gpsDevice;
}
/**
* Sets the wrapped gps device.
*/
public void setWrappedGpsDevice(ActiveMirrorGpsDevice gpsDevice) {
this.gpsDevice = gpsDevice;
setGpsDevice(gpsDevice);
}
/**
* Performs the actual mirror operation, delegating the action to the
* wrapped gps device.
*/
public void performMirroring() throws CompassGpsException {
checkDeviceSet();
this.gpsDevice.performMirroring();
}
/**
* If the scheduled timer whould work as a daemon thread or not.
*/
public boolean isDaemon() {
return daemon;
}
/**
* Sets if the scheduled timer would work as a daemon thread or not.
*/
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}
/**
* The period of the scheduled service in milli-seconds.
*/
public long getPeriod() {
return period;
}
/**
* Sets the period of the scheduled service in milli-seconds.
*
* @param period
*/
public void setPeriod(long period) {
this.period = period;
}
private class MirrorGpsDeviceThread extends Thread {
private long period;
private boolean canceled;
public MirrorGpsDeviceThread(long period) {
this.period = period;
}
public void run() {
while (!Thread.interrupted() || !canceled) {
try {
Thread.sleep(period);
} catch (InterruptedException e) {
break;
}
try {
gpsDevice.performMirroring();
} catch (Exception e) {
log.warn("Failed to perform gps device mirroring", e);
}
}
}
public void cancel() {
this.canceled = true;
this.interrupt();
}
}
}