/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.io.transport.cul.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.openhab.io.transport.cul.CULCommunicationException;
import org.openhab.io.transport.cul.CULDeviceException;
import org.openhab.io.transport.cul.CULMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class handles all CULHandler. You can only obtain CULHandlers via this
* manager.
*
* @author Till Klocke
* @since 1.4.0
*/
public class CULManager {
private final static Logger logger = LoggerFactory.getLogger(CULManager.class);
private static CULManager instance = new CULManager();
private Map<String, Class<? extends CULHandlerInternal<?>>> deviceTypeClasses = new HashMap<String, Class<? extends CULHandlerInternal<?>>>();
private Map<String, CULConfigFactory> deviceTypeConfigFactories = new HashMap<String, CULConfigFactory>();
private Map<String, CULHandlerInternal<?>> openDevices = new HashMap<String, CULHandlerInternal<?>>();
public static CULManager getInstance() {
return instance;
}
/**
* Retrieve the config factory for a certain device type.
*
* @param deviceType device type
* @return config factory for the device type
*/
public CULConfigFactory getConfigFactory(String deviceType) {
return deviceTypeConfigFactories.get(deviceType);
}
/**
* Get CULHandler for the given device in the given mode. The same
* CULHandler can be returned multiple times if you ask multiple times for
* the same device in the same mode. It is not possible to obtain a
* CULHandler of an already openend device for another RF mode.
*
* @param config
* The configuration for the handler.
* @return A CULHandler to communicate with the culfw based device.
* @throws CULDeviceException
*/
public <T extends CULConfig> CULHandlerInternal<T> getOpenCULHandler(T config) throws CULDeviceException {
CULMode mode = config.getMode();
String deviceName = config.getDeviceName();
logger.debug("Trying to open device " + deviceName + " in mode " + mode.toString());
synchronized (openDevices) {
if (openDevices.containsKey(deviceName)) {
@SuppressWarnings("unchecked")
CULHandlerInternal<T> handler = (CULHandlerInternal<T>) openDevices.get(deviceName);
if (handler.getConfig().equals(config)) {
logger.debug("Device " + deviceName + " is already open in mode " + mode.toString()
+ ", returning already openend handler");
return handler;
} else {
throw new CULDeviceException(
"The device " + deviceName + " is already open in mode " + mode.toString());
}
}
CULHandlerInternal<T> handler = createNewHandler(config);
openDevices.put(deviceName, handler);
return handler;
}
}
/**
* Return a CULHandler to the manager. The CULHandler will only be closed if
* there aren't any listeners registered with it. So it is save to call this
* methods as soon as you don't need the CULHandler any more.
*
* @param handler
*/
public void close(CULHandlerInternal<?> handler) {
if (handler != null) {
synchronized (openDevices) {
if (!handler.hasListeners()) {
openDevices.remove(handler);
try {
handler.send("X00");
} catch (CULCommunicationException e) {
logger.warn("Couldn't reset rf mode to X00");
}
handler.close();
} else {
logger.warn("Can't close device because it still has listeners");
}
}
}
}
public void registerHandlerClass(String deviceType, Class<? extends CULHandlerInternal<?>> clazz,
CULConfigFactory configFactory) {
logger.debug("Registering class " + clazz.getCanonicalName() + " for device type " + deviceType);
deviceTypeClasses.put(deviceType, clazz);
deviceTypeConfigFactories.put(deviceType, configFactory);
}
private <T extends CULConfig> CULHandlerInternal<T> createNewHandler(T config) throws CULDeviceException {
String deviceType = config.getDeviceType();
CULMode mode = config.getMode();
logger.debug("Searching class for device type " + deviceType);
@SuppressWarnings("unchecked")
Class<? extends CULHandlerInternal<T>> culHandlerclass = (Class<? extends CULHandlerInternal<T>>) deviceTypeClasses
.get(deviceType);
if (culHandlerclass == null) {
throw new CULDeviceException("No class for the device type " + deviceType + " is registred");
}
Class<?>[] constructorParametersTypes = { CULConfig.class };
Object[] parameters = { config };
try {
Constructor<? extends CULHandlerInternal<T>> culHanlderConstructor = culHandlerclass
.getConstructor(constructorParametersTypes);
CULHandlerInternal<T> culHandler = culHanlderConstructor.newInstance(parameters);
List<String> initCommands = mode.getCommands();
if (!(culHandler instanceof CULHandlerInternal)) {
logger.error(
"Class " + culHandlerclass.getCanonicalName() + " does not implement the internal interface");
throw new CULDeviceException("This CULHandler class does not implement the internal interface: "
+ culHandlerclass.getCanonicalName());
}
CULHandlerInternal<?> internalHandler = culHandler;
internalHandler.open();
for (String command : initCommands) {
internalHandler.sendWithoutCheck(command);
}
return culHandler;
} catch (SecurityException e1) {
throw new CULDeviceException("Not allowed to access the constructor ", e1);
} catch (NoSuchMethodException e1) {
throw new CULDeviceException("Can't find the constructor to build the CULHandler", e1);
} catch (IllegalArgumentException e) {
throw new CULDeviceException("Invalid arguments for constructor. CULConfig: " + config, e);
} catch (InstantiationException e) {
throw new CULDeviceException("Can't instantiate CULHandler object", e);
} catch (IllegalAccessException e) {
throw new CULDeviceException("Can't instantiate CULHandler object", e);
} catch (InvocationTargetException e) {
throw new CULDeviceException("Can't instantiate CULHandler object", e);
} catch (CULCommunicationException e) {
throw new CULDeviceException("Can't initialise RF mode", e);
}
}
}