package org.openhab.habclient;
import android.content.Context;
import android.util.Log;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.RequestParams;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.openhab.domain.IDocumentFactory;
import org.openhab.domain.IOpenHABWidgetProvider;
import org.openhab.domain.IRestCommunication;
import org.openhab.domain.model.OpenHABWidget;
import org.openhab.domain.model.OpenHABWidgetDataSource;
import org.openhab.domain.util.IColorParser;
import org.openhab.domain.util.ILogger;
import org.openhab.domain.util.StringHandler;
import org.openhab.habdroid.R;
import org.openhab.habdroid.core.DocumentHttpResponseHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.SocketTimeoutException;
import javax.inject.Inject;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
/**
* Created by Tony Alpskog in 2014.
*/
public class RestCommunication implements IRestCommunication {
private final ILogger mLogger;
private final IColorParser mColorParser;
private final IOpenHABSetting mOpenHABSetting;
private final IOpenHABWidgetProvider mWidgetProvider;
private final Context mContext;
private final IDocumentFactory mDocumentFactory;
private final AsyncHttpClient mAsyncHttpClient;
@Inject
public RestCommunication(Context context,
ILogger logger,
IColorParser colorParser,
IOpenHABSetting openHABSetting,
IOpenHABWidgetProvider widgetProvider,
IDocumentFactory documentFactory) {
if(context == null) throw new IllegalArgumentException("context is null");
if(logger == null) throw new IllegalArgumentException("logger is null");
if(colorParser == null) throw new IllegalArgumentException("colorParser is null");
if(openHABSetting == null) throw new IllegalArgumentException("openHABSetting is null");
if(widgetProvider == null) throw new IllegalArgumentException("widgetProvider is null");
if(documentFactory == null) throw new IllegalArgumentException("documentFactory is null");
mContext = context;
mLogger = logger;
mColorParser = colorParser;
mOpenHABSetting = openHABSetting;
mWidgetProvider = widgetProvider;
mDocumentFactory = documentFactory;
mAsyncHttpClient = mOpenHABSetting.createAsyncHttpClient();
}
@Override
public void requestOpenHABSitemap(final OpenHABWidget widget, final boolean longPolling, final Object ownerTag) {
if(widget != null && (widget.hasItem() || widget.hasLinkedPage()))
requestOpenHABSitemap(/*"https://demo.openhab.org:8443/rest/sitemaps/demo/" + */(widget.hasLinkedPage()? widget.getLinkedPage().getLink() : widget.getParent().getLinkedPage().getLink()), widget, longPolling, ownerTag);
else
mLogger.e(HABApplication.getLogTag(2), "[AsyncHttpClient] Sitemap cannot be requested due to missing sitemap data.");
}
@Override
public void requestOpenHABSitemap(final String sitemapUrl, final boolean longPolling, final Object ownerTag) {
requestOpenHABSitemap(sitemapUrl, null, longPolling, ownerTag);
}
@Override
public void requestOpenHABSitemap(final String sitemapUrl, final OpenHABWidget widget, final boolean longPolling, final Object ownerTag) {
final String RESTaddress;
if(StringHandler.isNullOrEmpty(sitemapUrl)) {
mLogger.w(HABApplication.getLogTag(), String.format("\n\r%s\n\r[AsyncHttpClient] Requested sitemap URL is %s", HABApplication.getLogTag(2), (sitemapUrl == null? "NULL": "empty")));
RESTaddress = mOpenHABSetting.getBaseUrl() + mContext.getString(R.string.openhab_demo_sitemap_postfix);
}
else {
RESTaddress = sitemapUrl;
}
mLogger.d(HABApplication.getLogTag(2), String.format("\n\r[AsyncHttpClient] Requested sitemap URL is '%s' longpolling = '%s'", sitemapUrl, longPolling));
final String callingMethod = HABApplication.getLogTag(2);
Header[] headers = {};
if (longPolling)
headers = new Header[] {new BasicHeader("X-Atmosphere-Transport", "long-polling")};
mLogger.d(HABApplication.getLogTag(), "[AsyncHttpClient] <" + ownerTag + "> is requesting REST data from: " + RESTaddress);
RequestParams rp = new RequestParams();
mAsyncHttpClient.get(mContext, RESTaddress, headers, null, new DocumentHttpResponseHandler(mDocumentFactory) {
@Override
public void onSuccess(Document document) {
if (document == null) {
mLogger.e(HABApplication.getLogTag(), "[AsyncHttpClient] " + RESTaddress + "\nshowAddUnitDialog() -> <\" + ownerTag + \"> Got a null response from openHAB");
return;
}
// tryPrintDocument(document, System.out);
mLogger.d(HABApplication.getLogTag(), String.format("\n\r%s - %s\n\r[AsyncHttpClient] DocumentHttpResponseHandler.onSuccess() for requested sitemap URL '%s' longpolling = '%s'", ownerTag, callingMethod, sitemapUrl, longPolling));
final Node rootNode = document.getFirstChild();
if (widget == null)
mWidgetProvider.setOpenHABWidgets(new OpenHABWidgetDataSource(rootNode, mLogger, mColorParser));
else
mWidgetProvider.setOpenHABWidgets(new OpenHABWidgetDataSource(rootNode, widget, mLogger, mColorParser));
checkForLongPolling();
}
@Override
public void onFailure(Throwable e, String errorResponse) {
mLogger.e(HABApplication.getLogTag(), "[AsyncHttpClient] " + RESTaddress + "\r\nget_items() - DocumentHttpResponseHandler.onFailure - " + e.toString());
if (e instanceof SocketTimeoutException) {
Log.d(HABApplication.getLogTag(), String.format("\n\r%s - %s\n\r[AsyncHttpClient] Connection timeout for requested sitemap URL '%s' longpolling = '%s'", ownerTag, callingMethod, sitemapUrl, longPolling));
}
checkForLongPolling();
}
private void checkForLongPolling() {
if(longPolling) {
Log.d(HABApplication.getLogTag(), String.format("\n\r%s - %s\n\r[AsyncHttpClient] LongPolling => Will run the sitemap request again for requested sitemap URL '%s' longpolling = '%s'", ownerTag, callingMethod, sitemapUrl, longPolling));
requestOpenHABSitemap(RESTaddress, longPolling, ownerTag);
}
}
}, ownerTag);
}
//TODO - Temporary method for debugging. Move this to another class [Tony Alpskog 2015-02-07]
public static boolean tryPrintDocument(Document doc, OutputStream out)/* throws IOException, TransformerException*/ {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(doc),
new StreamResult(new OutputStreamWriter(out, "UTF-8")));
return true;
} catch(Exception ex) {
Log.e("tryPrintDocument", "\n\rCould not print the XML document.");
return false;
}
}
@Override
public void cancelRequests(Object ownerTag) {
mAsyncHttpClient.cancelRequests(mContext, ownerTag, true);
}
}