package com.salama.android.developer; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.Locale; import MetoXML.XmlDeserializer; import MetoXML.XmlSerializer; import android.content.Context; import android.util.Log; import com.salama.android.dataservice.SalamaDataService; import com.salama.android.dataservice.SalamaDataServiceConfig; import com.salama.android.dataservice.WebService; import com.salama.android.developer.cloud.SalamaCloudService; import com.salama.android.developer.user.SalamaUserService; import com.salama.android.developer.util.SalamaWebService; import com.salama.android.developer.util.http.SalamaHttpClientUtil; import com.salama.android.jsservice.base.natives.SalamaNativeService; import com.salama.android.support.ServiceSupportApplication; import com.salama.android.support.ServiceSupportUtil; import com.salama.android.util.HexUtil; import com.salama.android.util.MD5Util; import com.salama.android.util.SSLog; import com.salama.android.util.http.HttpClientUtil; import com.salama.android.webcore.WebController; import com.salama.android.webcore.WebManager; public class SalamaAppService { public final static String EASY_APP_SERVICE_URI = "/easyApp/cloudDataService.do"; public final static String CLOUD_DATA_SERVICE_URI = "/cloudDataService.do"; public final static String EASY_APP_AUTH_SERVICE = "com.salama.easyapp.service.AppAuthService"; private final static String FILE_NAME_APP_AUTH_INFO_PREFIX = "salamaappauth_"; private final static String DEFAULT_WEB_PACKAGE_NAME = "html"; private final static int DEFAULT_HTTP_REQUEST_TIMEOUT_SECONDS = 30; private static File webBaseDir = null; private boolean _dedicatedServerMode = false; private String _appId = null; private String _appSecret = null; private String _bundleId = null; AppAuthInfo _appAuthInfo = null; AppInfo _appInfo = null; SalamaDataService _dataService = null; Object _lockForNewDataId = new Object(); //private String _easyAppServiceHttpHost = null; //private String _easyAppServiceHttpsHost = null; private String _easyAppServiceHostPrefix = null; private int _easyAppServiceHttpPort = 0; private int _easyAppServiceHttpsPort = 0; private String _easyAppServiceHttpUrl = null; private String _easyAppServiceHttpsUrl = null; private String _host = null; private String _myAppServiceHttpUrl = null; private String _myAppServiceHttpsUrl = null; //private String _udid = null; private String _systemLanguage = null; //private String _systemLanguagePrefix = null; private String _textFileName = null; private SalamaNativeService _nativeService = null; private WebService _webService = null; private boolean _notUseEasyAppService = false; private SalamaDataServiceConfig _config = null; private static SalamaAppService _singleton = null; public static void setWebBaseDir(File webBaseDir) { SalamaAppService.webBaseDir = webBaseDir; } public static SalamaAppService singleton() { if(_singleton == null) { _singleton = new SalamaAppService(); } return _singleton; } private SalamaAppService() { _bundleId = ServiceSupportApplication.singleton().getPackageName(); SSLog.i("SalamaAppService", "_bundleId:" + _bundleId); _appInfo = new AppInfo(); checkTextFile(); } private void initWebController() { //init web controller if(checkWebPackageExists(DEFAULT_WEB_PACKAGE_NAME)) { initObjsWithWebPackageName(DEFAULT_WEB_PACKAGE_NAME, "res"); } else { initObjsForMultiWebRootMode(); } } private boolean checkWebPackageExists(String webPackageName) { int htmlId = ServiceSupportApplication.singleton().getResources().getIdentifier( webPackageName, "raw", ServiceSupportApplication.singleton().getPackageName()); return htmlId != 0; } public String getAppId() { return _appId; } public String getAppSecret() { return _appSecret; } public String getBundleId() { return _bundleId; } public AppAuthInfo getAppAuthInfo() { return _appAuthInfo; } public AppInfo getAppInfo() { return _appInfo; } public SalamaDataService getDataService() { return _dataService; } public String getAppServiceHttpUrl() { return _easyAppServiceHttpUrl; } public String getAppServiceHttpsUrl() { return _easyAppServiceHttpsUrl; } /** * @return OpenUDID */ public String getUDID() { return SalamaApplication.getUDID(); } public String getSystemLanguage() { return _systemLanguage; } public WebService getWebService() { return _webService; } public SalamaUserService getUserService() { return SalamaUserService.singleton(); } public SalamaNativeService getNativeService() { return _nativeService; } public SalamaCloudService getCloudService() { return SalamaCloudService.singleton(); } public String getAppToken() { if(_appAuthInfo == null || _appAuthInfo.getAppToken() == null) { return ""; } else { return _appAuthInfo.getAppToken(); } } /** * 改变本地网页根路径 */ public void switchToWebRootDirPath(String webRootDirPath) { WebManager.getWebController().switchToWebRootDirPath(webRootDirPath); initServicesWithIsMultiWebRootMode(true); initWebService(); } /** * 初始化App. */ public void initApp() { initWebController(); _notUseEasyAppService = true; initWebService(); } /** * @param appId * @param appSecret */ public void initApp(String appId, String appSecret) { initWebController(); initAppWithNoAuthenticating(appId, appSecret); initWebService(); authenticateApp(); } /** * @param appId * @param appSecret */ public void initAppAsync(String appId, String appSecret) { initWebController(); initAppWithNoAuthenticating(appId, appSecret); initWebService(); _dataService.getQueueForQueryWebService().execute(new Runnable() { @Override public void run() { authenticateApp(); } }); } /** * 初始化App,独立服务器模式 * @param appId * @param appSecret */ public void initAppInDedicatedServerMode(String appId, String appSecret, String host, int port) { initWebController(); _dedicatedServerMode = true; _host = host; _easyAppServiceHttpPort = port; _easyAppServiceHttpsPort = 443; initWebService(); initAppWithNoAuthenticating(appId, appSecret); authenticateApp(); } /** * 初始化App,独立服务器模式,异步执行 * @param appId * @param appSecret */ public void initAppInDedicatedServerModeAsync(final String appId, final String appSecret, String host, int port) { initWebController(); _dedicatedServerMode = true; _host = host; _easyAppServiceHttpPort = port; _easyAppServiceHttpsPort = 443; initWebService(); _dataService.getQueueForQueryWebService().execute(new Runnable() { @Override public void run() { initAppWithNoAuthenticating(appId, appSecret); authenticateApp(); } }); } private void initAppHostInDedicatedServerMode() { _easyAppServiceHttpUrl = "http://" + _host + ":" + _easyAppServiceHttpPort + EASY_APP_SERVICE_URI; _easyAppServiceHttpsUrl = "https://" + _host + ":" + _easyAppServiceHttpPort + EASY_APP_SERVICE_URI; _myAppServiceHttpUrl = "http://" + _host + ":" + _easyAppServiceHttpPort + "/" + _appId + CLOUD_DATA_SERVICE_URI; _myAppServiceHttpsUrl = "https://" + _host + ":" + _easyAppServiceHttpsPort + "/" + _appId + CLOUD_DATA_SERVICE_URI; _appInfo.setAppServiceHttpUrl(_easyAppServiceHttpUrl); _appInfo.setAppServiceHttpsUrl(_easyAppServiceHttpsUrl); _appInfo.setMyAppServiceHttpUrl(_myAppServiceHttpUrl); _appInfo.setMyAppServiceHttpsUrl(_myAppServiceHttpsUrl); } public void initAppInDebugMode(String appId, String appSecret) { initAppWithNoAuthenticating(appId, appSecret); _easyAppServiceHttpPort = 8080; _easyAppServiceHttpsPort = 8080; _easyAppServiceHttpUrl = "http://127.0.0.1:8080" + EASY_APP_SERVICE_URI; _easyAppServiceHttpsUrl = _easyAppServiceHttpUrl; _appInfo.setAppServiceHttpUrl(_easyAppServiceHttpUrl); _appInfo.setAppServiceHttpsUrl(_easyAppServiceHttpsUrl); initWebService(); authenticateApp(); } /** * 认证AppId和AppSecret。认证失败的场合,则所有其他的WebService请求都会被拒绝 * @return true:成功 false:失败 */ public boolean appLogin() { AppAuthInfo appAuthInfo = appLoginByAppSecret(_appSecret); if(appAuthInfo != null) { if(isAppAuthInfoValid(appAuthInfo)) { if(_appAuthInfo == null) { _appAuthInfo = new AppAuthInfo(); _appAuthInfo.setAppId(appAuthInfo.getAppId()); } _appAuthInfo.setAppToken(appAuthInfo.getAppToken()); _appAuthInfo.setExpiringTime(appAuthInfo.getExpiringTime()); return true; } } return false; } /** * 取得text_xx.strings的内容,其中的xx为当前系统语言的2字符前缀。英语:en,简体汉语:zh,法语:fr,德语:de,日语:ja。 * 其他的语言参考IOS的文档中提供的链接 http://www.loc.gov/standards/iso639-2/php/English_list.php * 如果系统语言对应的text_xx.strings文件不存在,则读取text_en.strings。 * @param key text内容的key * @return text内容 */ public String getTextByKey(String key) { return ServiceSupportUtil.getStringsValueByKey(key, _textFileName); } /** * 生成dataId(可以作为本地数据库的数据主键) * 采用较为简单的方法:<udid> + <UTC>。方法内有锁,线程安全。但1秒只能产生1000个。 */ public String generateNewDataId() { synchronized (_lockForNewDataId) { try { Thread.sleep(1); } catch (InterruptedException e) { Log.e("SalamaAppService", "Error in generateNewDataId()", e); } return getUDID().concat(HexUtil.toHexString(System.currentTimeMillis())); } } private void checkTextFile() { _systemLanguage = Locale.getDefault().getLanguage(); SSLog.i("SalamaAppService", "_systemLanguage:" + _systemLanguage); //init .strings file _textFileName = "text_".concat(_systemLanguage.substring(0, 2)); int textFileId = ServiceSupportApplication.singleton().getResources().getIdentifier( _textFileName, "raw", ServiceSupportApplication.singleton().getPackageName()); if(textFileId == 0) { //file not exists SSLog.i("SalamaAppService", _textFileName + ".strings does not exist. Change to use text_en.strings"); _textFileName = "text_en"; textFileId = ServiceSupportApplication.singleton().getResources().getIdentifier( _textFileName, "raw", ServiceSupportApplication.singleton().getPackageName()); } SSLog.i("SalamaAppService", "_textFileName:" + _textFileName + " textFileId:" + textFileId); if(textFileId != 0) { try { ServiceSupportUtil.loadStringsFile(_textFileName, ServiceSupportApplication.singleton().getResources().openRawResource(textFileId)); } catch (Exception e) { Log.e("SalamaAppService", "Error in openning file:" + _textFileName, e); } } } private void initObjsWithWebPackageName(String webPackageName, String resourceDirName) { //DEBUG //WebController.setDebugMode(false); //init webmanager(include extract html) --------------- int htmlId = ServiceSupportApplication.singleton().getResources().getIdentifier( "html", "raw", ServiceSupportApplication.singleton().getPackageName()); SSLog.i("SalamaAppService", "htmlId:" + htmlId); InputStream htmlPackageStream = ServiceSupportApplication.singleton(). getResources().openRawResource(htmlId); try { if(webBaseDir == null) { WebManager.initWithWebPackageName(webPackageName, htmlPackageStream); } else { WebManager.initWithWebPackageName(webPackageName, htmlPackageStream, webBaseDir); } } catch(IOException e) { throw new RuntimeException(e); } initServicesWithIsMultiWebRootMode(false); // Register Service ---------------------------------- WebManager.getWebController().getNativeService().registerService("salama", this); } private void initObjsForMultiWebRootMode() { //DEBUG //WebController.setDebugMode(false); //init webmanager(include extract html) --------------- //init default htmlRootDir ---------------------------- if(webBaseDir == null) { webBaseDir = SalamaApplication.singleton().getExternalFilesDir(null); } File defaultHtmlRootDir = new File(webBaseDir, DEFAULT_WEB_PACKAGE_NAME); if(!defaultHtmlRootDir.exists()) { defaultHtmlRootDir.mkdirs(); } WebManager.initWithExistingWebRootDir(defaultHtmlRootDir); initServicesWithIsMultiWebRootMode(true); // Register Service ---------------------------------- WebManager.getWebController().getNativeService().registerService("salama", this); } private void initServicesWithIsMultiWebRootMode(boolean isMultiWebRootMode) { //init dataService ------------------------------------ _config = new SalamaDataServiceConfig(); _config.setHttpRequestTimeout(DEFAULT_HTTP_REQUEST_TIMEOUT_SECONDS); File resDir = new File(WebManager.getWebController().getWebRootDirPath(), "res"); _config.setResourceStorageDirPath(resDir.getAbsolutePath()); if(isMultiWebRootMode) { _config.setDbName("localDB" + "_" + WebManager.getWebController().getWebPackageName()); } else { _config.setDbName("localDB"); } _dataService = new SalamaDataService(_config); _nativeService = new SalamaNativeService(_dataService); } private void initWebService() { if(_notUseEasyAppService) { HttpClientUtil.initHttpParams( HttpClientUtil.DEFAULT_CONNECTION_POOL_TIMEOUT_MS, HttpClientUtil.DEFAULT_CONNECTION_TIMEOUT_MS, DEFAULT_HTTP_REQUEST_TIMEOUT_SECONDS, HttpClientUtil.DEFAULT_HTTP_PORT, HttpClientUtil.DEFAULT_HTTPS_PORT); _webService = new WebService(); } else { SalamaHttpClientUtil.initHttpParams( SalamaHttpClientUtil.DEFAULT_CONNECTION_POOL_TIMEOUT_MS, SalamaHttpClientUtil.DEFAULT_CONNECTION_TIMEOUT_MS, DEFAULT_HTTP_REQUEST_TIMEOUT_SECONDS, _easyAppServiceHttpPort, _easyAppServiceHttpsPort); _webService = new SalamaWebService(); } _webService.setRequestTimeoutSeconds(_config.getHttpRequestTimeout()); _webService.setResourceFileManager(_dataService.getResourceFileManager()); } private void initAppWithNoAuthenticating(String appId, String appSecret) { _appId = appId; _appSecret = appSecret; if(_dedicatedServerMode) { initAppHostInDedicatedServerMode(); } else { initAppHost(); } } private void initAppHost() { String serverDivisionNum = _appId.substring(0, 4); _easyAppServiceHostPrefix = "dev".concat(serverDivisionNum); String serverNumHex = _appId.substring(4, 6); byte serverNumByte = Byte.parseByte(serverNumHex, 16); _easyAppServiceHttpPort = 30000 + serverNumByte; _easyAppServiceHttpsPort = 40000 + serverNumByte; _easyAppServiceHttpUrl = "http://".concat(_easyAppServiceHostPrefix).concat(".salama.com.cn:") .concat(Integer.toString(_easyAppServiceHttpPort)).concat(EASY_APP_SERVICE_URI); _easyAppServiceHttpsUrl = "https://".concat(_easyAppServiceHostPrefix).concat(".salama.com.cn:") .concat(Integer.toString(_easyAppServiceHttpsPort)).concat(EASY_APP_SERVICE_URI); _appInfo.setAppServiceHttpUrl(_easyAppServiceHttpUrl); _appInfo.setAppServiceHttpsUrl(_easyAppServiceHttpsUrl); // SSLog.d("SalamaAppService", "initAppHost() http url:" + _easyAppServiceHttpUrl); // SSLog.d("SalamaAppService", "initAppHost() https url:" + _easyAppServiceHttpsUrl); } private AppAuthInfo authenticateApp() { //login boolean loginSuccess = checkAppLogin(); if(!loginSuccess) { SSLog.i("SalamaAppService", "App login failed"); } // _appInfo.setAppId(_appAuthInfo.getAppId()); // _appInfo.setAppToken(_appAuthInfo.getAppToken()); // _appInfo.setExpiringTime(_appAuthInfo.getExpiringTime()); return _appAuthInfo; } private boolean checkAppLogin() { boolean loginSuccess = false; _appAuthInfo = getStoredAppAuthInfoWithAppId(_appId); if(_appAuthInfo != null && (_appAuthInfo.getExpiringTime() - 120000) >= System.currentTimeMillis() && _appAuthInfo.getAppToken() != null ) { //token尚未过期 AppAuthInfo appAuthInfo = appLoginByAppToken(_appAuthInfo.getAppToken()); if(appAuthInfo != null) { _appAuthInfo.setAppToken(appAuthInfo.getAppToken()); _appAuthInfo.setExpiringTime(appAuthInfo.getExpiringTime()); if(isAppAuthInfoValid(appAuthInfo)) { loginSuccess = true; } } } if(!loginSuccess) { AppAuthInfo appAuthInfo = appLoginByAppSecret(_appSecret); if(appAuthInfo != null) { if(isAppAuthInfoValid(appAuthInfo)) { if(_appAuthInfo == null) { _appAuthInfo = new AppAuthInfo(); _appAuthInfo.setAppId(appAuthInfo.getAppId()); } _appAuthInfo.setAppToken(appAuthInfo.getAppToken()); _appAuthInfo.setExpiringTime(appAuthInfo.getExpiringTime()); loginSuccess = true; } } else { if(_appAuthInfo == null) { _appAuthInfo = new AppAuthInfo(); } } } if(!loginSuccess) { if(_appAuthInfo != null) { _appAuthInfo.setAppToken(""); _appAuthInfo.setExpiringTime(0); } } if(_appAuthInfo != null) { storeAppAuthInfoWithAppId(_appId, _appAuthInfo); } return loginSuccess; } private AppAuthInfo appLoginByAppToken(String appToken) { try { String appAuthInfoXml = (String) SalamaHttpClientUtil.doBasicGet(false, _easyAppServiceHttpsUrl, ServiceSupportUtil.newList(new String[]{"serviceType", "serviceMethod", "appId", "appToken"}), ServiceSupportUtil.newList(new String[]{EASY_APP_AUTH_SERVICE, "appLoginByToken", _appId, appToken}), null); SSLog.d("SalamaAppService", "appLoginByAppToken() webservice result:" + appAuthInfoXml); if(appAuthInfoXml == null || appAuthInfoXml.length() == 0) { return null; } else { return (AppAuthInfo) XmlDeserializer.stringToObject(appAuthInfoXml, AppAuthInfo.class, ServiceSupportApplication.singleton()); } } catch (Exception e) { Log.e("SalamaAppService", "Error in appLoginByAppToken()", e); return null; } } private AppAuthInfo getStoredAppAuthInfoWithAppId(String appId) { FileInputStream fis = null; InputStreamReader reader = null; try { fis = ServiceSupportApplication.singleton().openFileInput( FILE_NAME_APP_AUTH_INFO_PREFIX.concat(appId)); reader = new InputStreamReader(fis, XmlDeserializer.DefaultCharset); XmlDeserializer xmlDes = new XmlDeserializer(); return (AppAuthInfo) xmlDes.Deserialize( reader, AppAuthInfo.class, ServiceSupportApplication.singleton()); } catch(FileNotFoundException e) { SSLog.d("SalamaAppService", "getStoredAppAuthInfoWithAppId() file does not exist."); return null; } catch(Exception e) { Log.e("SalamaAppService", "Error in getStoredAppAuthInfoWithAppId()", e); return null; } finally { try { fis.close(); } catch(Exception e) { } try { reader.close(); } catch(Exception e) { } } } private void storeAppAuthInfoWithAppId(String appId, AppAuthInfo appAuthInfo) { FileOutputStream fos = null; OutputStreamWriter writer = null; try { fos = ServiceSupportApplication.singleton().openFileOutput( FILE_NAME_APP_AUTH_INFO_PREFIX.concat(appId), Context.MODE_PRIVATE); writer = new OutputStreamWriter(fos, XmlDeserializer.DefaultCharset); XmlSerializer xmlSer = new XmlSerializer(); xmlSer.Serialize(writer, appAuthInfo, AppAuthInfo.class); writer.flush(); } catch(Exception e) { Log.e("SalamaAppService", "Error in storeAppAuthInfoWithAppId()", e); } finally { try { fos.close(); } catch(Exception e) { } try { writer.close(); } catch(Exception e) { } } } private boolean isAppAuthInfoValid(AppAuthInfo appAuthInfo) { if(appAuthInfo == null) { return false; } else { if(!_appId.equals(appAuthInfo.getAppId())) { return false; } if(appAuthInfo.getAppToken() == null) { return false; } if(appAuthInfo.getExpiringTime() <= System.currentTimeMillis()) { return false; } return true; } } private AppAuthInfo appLoginByAppSecret(String appSecret) { String utcTimeStr = Long.toString(System.currentTimeMillis()); String utcTimeMD5 = MD5Util.md5String(utcTimeStr); String secretMD5 = MD5Util.md5String(appSecret); String secretMD5MD5 = MD5Util.md5String(secretMD5.concat(utcTimeMD5)); try { String appAuthInfoXml = (String)SalamaHttpClientUtil.doBasicGet(false, _easyAppServiceHttpsUrl, ServiceSupportUtil.newList(new String[]{"serviceType", "serviceMethod", "appId", "appSecretMD5MD5", "utcTime"}), ServiceSupportUtil.newList(new String[]{EASY_APP_AUTH_SERVICE, "appLogin", _appId, secretMD5MD5, utcTimeStr}), null); SSLog.d("SalamaAppService", "appLoginByAppSecret() webservice result:" + appAuthInfoXml); if(appAuthInfoXml == null || appAuthInfoXml.length() == 0) { return null; } else { return (AppAuthInfo)XmlDeserializer.stringToObject(appAuthInfoXml, AppAuthInfo.class); } } catch (Exception e) { Log.e("SalamaAppService", "Error in appLoginByAppSecret()", e); return null; } } }