<commit_before>/**
 * Copyright (c) 2010-2014, openHAB.org and others.
 *
 * 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.binding.fritzbox.internal;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.telnet.TelnetClient;
import org.openhab.binding.fritzbox.FritzboxBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.openhab.library.tel.types.CallType;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * The FritzBox binding connects to a AVM FritzBox on the monitor port 1012 and
 * listens to event notifications from this box. There are event for incoming
 * and outgoing calls, as well as for connections and disconnections.
 * 
 * @author <NAME>
 * @since 0.7.0
 */
public class FritzboxBinding extends
		AbstractActiveBinding<FritzboxBindingProvider> implements
		ManagedService {

	private static HashMap<String, String> commandMap = new HashMap<String, String>();
	private static HashMap<String, String> queryMap = new HashMap<String, String>();

	static {
		commandMap.put(FritzboxBindingProvider.TYPE_DECT,
				"ctlmgr_ctl w dect settings/enabled");
		commandMap.put(FritzboxBindingProvider.TYPE_WLAN,
				"ctlmgr_ctl w wlan settings/ap_enabled");

		queryMap.put(FritzboxBindingProvider.TYPE_DECT,
				"ctlmgr_ctl r dect settings/enabled");
		queryMap.put(FritzboxBindingProvider.TYPE_WLAN,
				"ctlmgr_ctl r wlan settings/ap_enabled");
	}

	@Override
	public void bindingChanged(BindingProvider provider, String itemName) {
		super.bindingChanged(provider, itemName);

		conditionalDeActivate();

	}

	private void conditionalDeActivate() {
		logger.info("Fritzbox conditional deActivate: {}", bindingsExist());

		if (bindingsExist()) {
			activate();
		} else {
			deactivate();
		}
	}

	private static final Logger logger = LoggerFactory
			.getLogger(FritzboxBinding.class);

	protected static final int MONITOR_PORT = 1012;

	/** the current thread instance that is listening to the FritzBox */
	protected static MonitorThread monitorThread = null;

	/* The IP address to connect to */
	protected static String ip;

	/* The password of the FritzBox to access via Telnet */
	protected static String password;

	/**
	 * Reference to this instance to be used with the reconnection job which is
	 * static.
	 */
	private static FritzboxBinding INSTANCE;

	public FritzboxBinding() {
		INSTANCE = this;
	}

	public void activate() {
		super.activate();
		setProperlyConfigured(true);
		// if bundle is already configured, launch the monitor thread right away
		if (ip != null) {
			reconnect();
		}
	}

	public void deactivate() {
		if (monitorThread != null) {
			monitorThread.interrupt();
		}
		monitorThread = null;
	}

	@Override
	public void internalReceiveCommand(String itemName, Command command) {

		if (password != null) {
			String type = null;
			for (FritzboxBindingProvider provider : providers) {
				type = provider.getType(itemName);
				if (type != null) {
					break;
				}
			}

			logger.info("Fritzbox type: {}", type);

			if (type == null)
				return;

			TelnetCommandThread thread = new TelnetCommandThread(type, command);
			thread.start();
			
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("rawtypes")
	public void updated(Dictionary config) throws ConfigurationException {

		if (config != null) {
			String ip = (String) config.get("ip");
			if (StringUtils.isNotBlank(ip)) {
				if (!ip.equals(FritzboxBinding.ip)) {
					// only do something if the ip has changed
					FritzboxBinding.ip = ip;
					conditionalDeActivate();

					// schedule a daily reconnection as sometimes the FritzBox
					// stops sending data
					// and thus blocks the monitor thread
					try {
						Scheduler sched = StdSchedulerFactory
								.getDefaultScheduler();
						JobDetail job = newJob(ReconnectJob.class)
								.withIdentity("Reconnect", "FritzBox").build();

						CronTrigger trigger = newTrigger()
								.withIdentity("Reconnect", "FritzBox")
								.withSchedule(
										CronScheduleBuilder
												.cronSchedule("0 0 0 * * ?"))
								.build();

						sched.scheduleJob(job, trigger);
						logger.debug("Scheduled a daily reconnection to FritzBox on {}:{}", ip, MONITOR_PORT);
					} catch (SchedulerException e) {
						logger.warn("Could not create daily reconnection job", e);
					}
				}
			}
			String password = (String) config.get("password");
			if (StringUtils.isNotBlank(password)) {
				FritzboxBinding.password = password;
			}
		}
	}

	protected void reconnect() {
		if (monitorThread != null) {
			// let's end the old thread
			monitorThread.interrupt();
			monitorThread = null;
		}
		// create a new thread for listening to the FritzBox
		monitorThread = new MonitorThread(this.eventPublisher, this.providers);
		monitorThread.start();
	}
<commit_msg>[untested] fix to stop reconnect job being rescheduled<commit_after>/**
 * Copyright (c) 2010-2014, openHAB.org and others.
 *
 * 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.binding.fritzbox.internal;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.JobKey.jobKey;
import static org.quartz.TriggerBuilder.newTrigger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.telnet.TelnetClient;
import org.openhab.binding.fritzbox.FritzboxBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.openhab.library.tel.types.CallType;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * The FritzBox binding connects to a AVM FritzBox on the monitor port 1012 and
 * listens to event notifications from this box. There are event for incoming
 * and outgoing calls, as well as for connections and disconnections.
 * 
 * @author <NAME>
 * @since 0.7.0
 */
public class FritzboxBinding extends
		AbstractActiveBinding<FritzboxBindingProvider> implements
		ManagedService {

	private static HashMap<String, String> commandMap = new HashMap<String, String>();
	private static HashMap<String, String> queryMap = new HashMap<String, String>();

	static {
		commandMap.put(FritzboxBindingProvider.TYPE_DECT,
				"ctlmgr_ctl w dect settings/enabled");
		commandMap.put(FritzboxBindingProvider.TYPE_WLAN,
				"ctlmgr_ctl w wlan settings/ap_enabled");

		queryMap.put(FritzboxBindingProvider.TYPE_DECT,
				"ctlmgr_ctl r dect settings/enabled");
		queryMap.put(FritzboxBindingProvider.TYPE_WLAN,
				"ctlmgr_ctl r wlan settings/ap_enabled");
	}

	@Override
	public void bindingChanged(BindingProvider provider, String itemName) {
		super.bindingChanged(provider, itemName);

		conditionalDeActivate();

	}

	private void conditionalDeActivate() {
		logger.info("Fritzbox conditional deActivate: {}", bindingsExist());

		if (bindingsExist()) {
			activate();
		} else {
			deactivate();
		}
	}

	private static final Logger logger = LoggerFactory
			.getLogger(FritzboxBinding.class);

	protected static final int MONITOR_PORT = 1012;

	/** the current thread instance that is listening to the FritzBox */
	protected static MonitorThread monitorThread = null;

	/* The IP address to connect to */
	protected static String ip;

	/* The password of the FritzBox to access via Telnet */
	protected static String password;

	/**
	 * Reference to this instance to be used with the reconnection job which is
	 * static.
	 */
	private static FritzboxBinding INSTANCE;

	public FritzboxBinding() {
		INSTANCE = this;
	}

	public void activate() {
		super.activate();
		setProperlyConfigured(true);
		// if bundle is already configured, launch the monitor thread right away
		if (ip != null) {
			reconnect();
		}
	}

	public void deactivate() {
		if (monitorThread != null) {
			monitorThread.interrupt();
		}
		monitorThread = null;
	}

	@Override
	public void internalReceiveCommand(String itemName, Command command) {

		if (password != null) {
			String type = null;
			for (FritzboxBindingProvider provider : providers) {
				type = provider.getType(itemName);
				if (type != null) {
					break;
				}
			}

			logger.info("Fritzbox type: {}", type);

			if (type == null)
				return;

			TelnetCommandThread thread = new TelnetCommandThread(type, command);
			thread.start();
			
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("rawtypes")
	public void updated(Dictionary config) throws ConfigurationException {

		if (config != null) {
			String ip = (String) config.get("ip");
			if (StringUtils.isNotBlank(ip)) {
				if (!ip.equals(FritzboxBinding.ip)) {
					// only do something if the ip has changed
					FritzboxBinding.ip = ip;
					conditionalDeActivate();
                    
					// schedule a daily reconnection as sometimes the FritzBox
					// stops sending data
					// and thus blocks the monitor thread
					try {
						Scheduler sched = StdSchedulerFactory
								.getDefaultScheduler();
                                
                        JobKey jobKey = jobKey("Reconnect", "FritzBox");
                        if (sched.checkExists(jobKey)) {
                            logger.debug("Daily reconnection job already exists");
                        } else {
                            JobDetail job = newJob(ReconnectJob.class)
                                    .withIdentity(jobKey).build();

                            CronTrigger trigger = newTrigger()
                                    .withIdentity("Reconnect", "FritzBox")
                                    .withSchedule(
                                            CronScheduleBuilder
                                                    .cronSchedule("0 0 0 * * ?"))
                                    .build();

                            sched.scheduleJob(job, trigger);
                            logger.debug("Scheduled a daily reconnection to FritzBox on {}:{}", ip, MONITOR_PORT);
                        }
					} catch (SchedulerException e) {
						logger.warn("Could not create daily reconnection job", e);
					}
				}
			}
			String password = (String) config.get("password");
			if (StringUtils.isNotBlank(password)) {
				FritzboxBinding.password = password;
			}
		}
	}

	protected void reconnect() {
		if (monitorThread != null) {
			// let's end the old thread
			monitorThread.interrupt();
			monitorThread = null;
		}
		// create a new thread for listening to the FritzBox
		monitorThread = new MonitorThread(this.eventPublisher, this.providers);
		monitorThread.start();
	}

{'fraction_non_alphanumeric': 0.10052707967305782, 'fraction_numerical': 0.00427774807119395, 'mean_word_length': 3.463689055574497, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '989405', 'n_tokens_mistral': 4310, 'n_tokens_neox': 3737, 'n_words': 991}