/**
* This file Copyright (c) 2005-2010 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain Eclipse Public Licensed code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.subscription;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.activities.IWorkbenchActivitySupport;
import org.eclipse.ui.progress.UIJob;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.model.AuthUtils;
import com.aptana.ide.core.model.IModelListener;
import com.aptana.ide.core.model.IModifiableObject;
import com.aptana.ide.core.model.user.AptanaUser;
import com.aptana.ide.core.model.user.User;
import com.aptana.ide.core.ui.CoreUIUtils;
import com.aptana.ide.subscription.model.SubscriptionService;
public class SubscriptionManager {
private static final String SUBSCRIPTION_URL = "http://staging-aptanalivestaging.aptanacloud.com/subscriptions.xml"; //$NON-NLS-1$
private static final String SECURE_PREF_NODE_SERVICES = SubscriptionPlugin.PLUGIN_ID
+ "/services"; //$NON-NLS-1$
private static final String SECURE_PREF_NODE_USER = SubscriptionPlugin.PLUGIN_ID
+ "/user"; //$NON-NLS-1$
private static final String PREF_LAST_USER = "LastSignedInUser"; //$NON-NLS-1$
private static final long DAY_TIME = 1000 * 60 * 60 * 24;
// make the default 30 days for requiring user to sign in
private static final int ALLOWED_DAY = 30;
// grace period of 7 days before the activities are truly turned off
private static final int GRACE_PERIOD = 7;
private static final Set<String> EMPTY_SET = new HashSet<String>();
private static SubscriptionManager fManager = null;
private static IWorkbenchActivitySupport activitySupport = PlatformUI
.getWorkbench().getActivitySupport();;
private User fUser;
private String fCurrentUsername;
private IModelListener fModelListener = new IModelListener() {
public void modelChanged(IModifiableObject object) {
if (object instanceof User) {
if (fUser.hasCredentials()) {
// saves the current user
fCurrentUsername = fUser.getUsername();
getPreferenceStore().setValue(PREF_LAST_USER,
fCurrentUsername);
// updates the subscriptions
update();
} else {
// user signed out
// stores the time for the 30-day check
ISecurePreferences prefs = getUserSecurePreferences();
try {
prefs.putLong(fCurrentUsername, (new Date()).getTime(),
true);
} catch (StorageException e) {
logError(e);
}
}
}
}
};
public static SubscriptionManager getInstance() {
if (fManager == null) {
fManager = new SubscriptionManager();
}
return fManager;
}
public void init() {
fUser.addListener(fModelListener);
}
public void shutdown() {
fUser.removeListener(fModelListener);
}
public void update() {
try {
// access the latest remote content for the current user
URL url = getURL(SUBSCRIPTION_URL);
URLConnection connection = url.openConnection();
connection.setUseCaches(false);
connection.setConnectTimeout(1000);
connection.addRequestProperty("Cache-Control", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$
connection.addRequestProperty("Authorization", AuthUtils //$NON-NLS-1$
.getAuthorizationHeader(fUser.getUsername(), fUser
.getPassword()));
InputStream in = connection.getInputStream();
StringBuilder text = new StringBuilder();
byte[] bytes = new byte[1024];
BufferedInputStream input = new BufferedInputStream(in);
int bytesRead;
while ((bytesRead = input.read(bytes)) != -1) {
text.append(new String(bytes, 0, bytesRead));
}
// parses the content
String content = text.toString();
setEnabledActivityIds(parseXML(content));
// saves the latest content
ISecurePreferences prefs = getServicesSecurePreferences();
prefs.put(fUser.getUsername(), content, true);
} catch (IOException e) {
// Failed to grab the remote content; falls back to the cached
// version
setEnabledActivityIds(parseCache(fUser.getUsername()));
} catch (StorageException e) {
logError(e);
}
}
private SubscriptionManager() {
fUser = AptanaUser.getSignedInUser();
if (fUser.hasCredentials()) {
// user is already signed in
fCurrentUsername = fUser.getUsername();
update();
} else {
fCurrentUsername = getPreferenceStore().getString(PREF_LAST_USER);
// finds out when was the last time the previous user was logged in
ISecurePreferences prefs = getUserSecurePreferences();
try {
long lastSignedOutTime = prefs.getLong(fCurrentUsername, 0);
if (lastSignedOutTime == 0) {
// never signed in; turns off all Live activities
setEnabledActivityIds(new HashSet<String>());
} else {
Set<String> activityIds = parseCache(fCurrentUsername);
if (activityIds.isEmpty()) {
// user has not subscribed to any Live features, so no
// need to warn
return;
}
long currentTime = (new Date()).getTime();
if (currentTime - lastSignedOutTime >= (ALLOWED_DAY + GRACE_PERIOD)
* DAY_TIME) {
// grace period is passed; turns off all Live activities
setEnabledActivityIds(new HashSet<String>());
} else if (currentTime - lastSignedOutTime >= ALLOWED_DAY
* DAY_TIME) {
// warns the user to log in or otherwise the Live
// activities will be turned off
showWarning();
} else {
setEnabledActivityIds(activityIds);
}
}
} catch (StorageException e) {
logError(e);
}
}
}
private Set<String> parseXML(String content) {
try {
XMLReader reader = SAXParserFactory.newInstance().newSAXParser()
.getXMLReader();
SubscriptionContentHandler handler = new SubscriptionContentHandler();
reader.setContentHandler(handler);
reader.parse(new InputSource(new StringReader(content)));
final Set<String> activityIds = new HashSet<String>();
SubscriptionService[] services = handler.getServices();
String[] ids;
for (SubscriptionService service : services) {
ids = service.getActivityIds();
for (String id : ids) {
activityIds.add(id);
}
}
return activityIds;
} catch (SAXException e) {
logError(e);
} catch (ParserConfigurationException e) {
logError(e);
} catch (IOException e) {
logError(e);
}
return EMPTY_SET;
}
private Set<String> parseCache(String username) {
ISecurePreferences prefs = getServicesSecurePreferences();
try {
String content = prefs.get(username, null);
if (content != null) {
return parseXML(content);
}
} catch (StorageException se) {
logError(se);
}
return EMPTY_SET;
}
/**
* Specify a set of activities to be enabled.
*
* @param ids
* the set of activity ids being enabled
*/
private void setEnabledActivityIds(final Set<String> ids) {
Job job = new UIJob("Enabling Live Activities") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
activitySupport.setEnabledActivityIds(ids);
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
private static void showWarning() {
CoreUIUtils.getDisplay().asyncExec(new Runnable() {
public void run() {
MessageDialog.openWarning(CoreUIUtils.getActiveShell(),
Messages.SubscriptionManager_WarningTitle,
MessageFormat.format(
Messages.SubscriptionManager_WarningMessage,
ALLOWED_DAY));
}
});
}
private static ISecurePreferences getServicesSecurePreferences() {
ISecurePreferences root = SecurePreferencesFactory.getDefault();
ISecurePreferences node = root.node(SECURE_PREF_NODE_SERVICES);
return node;
}
private static ISecurePreferences getUserSecurePreferences() {
ISecurePreferences root = SecurePreferencesFactory.getDefault();
ISecurePreferences node = root.node(SECURE_PREF_NODE_USER);
return node;
}
private static IPreferenceStore getPreferenceStore() {
return SubscriptionPlugin.getDefault().getPreferenceStore();
}
private static URL getURL(String location) throws MalformedURLException {
try {
return new URL(location);
} catch (MalformedURLException e) {
return (new File(location)).toURI().toURL();
}
}
private static void logError(Exception e) {
IdeLog.logError(SubscriptionPlugin.getDefault(), e.getMessage(), e);
}
}