Java tutorial
/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wso2.carbon.dataservices.sql.driver.util; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.gdata.client.Query; import com.google.gdata.client.spreadsheet.SpreadsheetService; import com.google.gdata.data.IEntry; import com.google.gdata.data.IFeed; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.dataservices.sql.driver.TDriverUtil; import org.wso2.carbon.dataservices.sql.driver.internal.SQLDriverDSComponent; import org.wso2.carbon.dataservices.sql.driver.parser.Constants; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.Resource; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.sql.SQLException; /** * Helper class to manipulate feed requests with access tokens */ public class GSpreadFeedProcessor { private static final Log log = LogFactory.getLog(GSpreadFeedProcessor.class); private String clientId; private String clientSecret; private String accessToken; private String refreshToken; private String visibility = Constants.ACCESS_MODE_PRIVATE; private SpreadsheetService service; public void setClientId(String clientId) { this.clientId = clientId; } public String getClientId() { return clientId; } public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } public String getClientSecret() { return clientSecret; } public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } public String getRefreshToken() { return refreshToken; } public void setService(SpreadsheetService service) { this.service = service; } public SpreadsheetService getService() { return service; } private String charSetType = "UTF-8"; private String baseRegistryOauthTokenPath = "/repository/components/org.wso2.carbon.dataservices.sql.driver/tokens/"; public GSpreadFeedProcessor(String clientId, String clientSecret, String refReshToken, String visibility, String baseRegistryOauthTokenPath) throws SQLException { this.clientId = clientId; this.clientSecret = clientSecret; this.refreshToken = refReshToken; this.visibility = visibility; if (!this.checkVisibility()) { throw new SQLException("Invalid access mode '" + visibility + "' is provided"); } if (requiresAuth()) { if (this.clientId == null || this.clientId.isEmpty()) { throw new SQLException("Valid Client id not provided"); } if (this.clientSecret == null || this.clientSecret.isEmpty()) { throw new SQLException("Valid Client secret not provided"); } if (this.refreshToken == null || this.refreshToken.isEmpty()) { throw new SQLException("Valid refresh token not provided"); } try { this.clientId = URLDecoder.decode(this.clientId, "UTF-8"); this.clientSecret = URLDecoder.decode(this.clientSecret, "UTF-8"); this.refreshToken = URLDecoder.decode(this.refreshToken, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new SQLException("Error in retrieving Authentication information " + e.getMessage(), e); } } this.baseRegistryOauthTokenPath = baseRegistryOauthTokenPath; } public <E extends IEntry> E insert(URL feedUrl, E entry) throws SQLException { try { if (this.requiresAuth()) { if (this.accessToken != null) { this.authenticateWithAccessToken(); try { return this.service.insert(feedUrl, entry); } catch (Exception e) { log.warn("GSpreadConfig.getFeed(): Failed to retrieve Feeds with current AccessToken ", e); } String accessTokenFromRegistry = this.getAccessTokenFromRegistry(); if (accessTokenFromRegistry != null && this.accessToken != accessTokenFromRegistry) { this.accessToken = accessTokenFromRegistry; this.authenticateWithAccessToken(); try { return this.service.insert(feedUrl, entry); } catch (Exception e) { log.warn( "GSpreadConfig.getFeed(): Failed to retrieve Feeds with AccessToken from registry ", e); } } } this.refreshAndAuthenticate(); this.saveTokenToRegistry(); } return this.service.insert(feedUrl, entry); } catch (Exception e) { throw new SQLException("Error in retrieving Feed data " + e.getMessage(), e); } } /** * this method has the logic implemented to use access token to access spreadsheet api * and it will be shared between cluster nodes via registry as well. this will refresh the access token * if access tokens stored in memory and registry are expired, then it will store the new access token * in registry so that it will be shared among nodes * * @param feedUrl * @param feedClass * @param <F> * @return feed * @throws Exception */ public <F extends IFeed> F getFeed(URL feedUrl, Class<F> feedClass) throws SQLException { try { if (this.requiresAuth()) { if (this.accessToken != null) { this.authenticateWithAccessToken(); try { return this.service.getFeed(feedUrl, feedClass); } catch (Exception e) { log.warn("GSpreadConfig.getFeed(): Failed to retrieve Feeds with current AccessToken ", e); } String accessTokenFromRegistry = this.getAccessTokenFromRegistry(); if (accessTokenFromRegistry != null && this.accessToken != accessTokenFromRegistry) { this.accessToken = accessTokenFromRegistry; this.authenticateWithAccessToken(); try { return this.service.getFeed(feedUrl, feedClass); } catch (Exception e) { log.warn( "GSpreadConfig.getFeed(): Failed to retrieve Feeds with AccessToken from registry ", e); } } } this.refreshAndAuthenticate(); this.saveTokenToRegistry(); } return this.service.getFeed(feedUrl, feedClass); } catch (Exception e) { throw new SQLException("Error in retrieving Feed data " + e.getMessage(), e); } } /** * this method has the logic implemented to use access token to access spreadsheet api * and it will be shared between cluster nodes via registry as well. this will refresh the access token * if access tokens stored in memory and registry are expired, then it will store the new access token * in registry so that it will be shared among nodes * * @param query * @param feedClass * @param <F> * @return feed * @throws Exception */ public <F extends IFeed> F getFeed(Query query, Class<F> feedClass) throws SQLException { try { if (this.requiresAuth()) { if (this.accessToken != null) { this.authenticateWithAccessToken(); try { return this.service.getFeed(query, feedClass); } catch (Exception e) { log.warn("GSpreadConfig.getFeed(): Failed to retrieve Feeds with current AccessToken ", e); } String accessTokenFromRegistry = this.getAccessTokenFromRegistry(); if (accessTokenFromRegistry != null && this.accessToken != accessTokenFromRegistry) { this.accessToken = accessTokenFromRegistry; this.authenticateWithAccessToken(); try { return this.service.getFeed(query, feedClass); } catch (Exception e) { log.warn( "GSpreadConfig.getFeed(): Failed to retrieve Feeds with AccessToken from registry ", e); } } } this.refreshAndAuthenticate(); this.saveTokenToRegistry(); } return this.service.getFeed(query, feedClass); } catch (Exception e) { throw new SQLException("Error in retrieving Feed data " + e.getMessage(), e); } } /** * helper method to authenticate using just access token */ private void authenticateWithAccessToken() { GoogleCredential credential = getBaseCredential(); credential.setAccessToken(this.accessToken); this.service.setOAuth2Credentials(credential); } /** * helper method to refresh the access token and authenticate * * @throws Exception */ private void refreshAndAuthenticate() throws Exception { GoogleCredential credential = getBaseCredential(); credential.setAccessToken(this.accessToken); credential.setRefreshToken(this.refreshToken); credential.refreshToken(); this.accessToken = credential.getAccessToken(); this.service.setOAuth2Credentials(credential); } /** * helper method to get the base credential object * * @return credential */ private GoogleCredential getBaseCredential() { HttpTransport httpTransport = new NetHttpTransport(); JacksonFactory jsonFactory = new JacksonFactory(); GoogleCredential credential = new GoogleCredential.Builder() .setClientSecrets(this.clientId, this.clientSecret).setTransport(httpTransport) .setJsonFactory(jsonFactory).build(); return credential; } private String generateAuthTokenResourcePath() { // StringBuilder userKey = new StringBuilder(); // /* append the username value 3 times because, // * later when we do base64 encoding, we have to be sure, // * it doesn't have "=" characters by making the source data // * a multiple of 3, thus not to have any padding data. // */ String resPath = this.baseRegistryOauthTokenPath + "configs/" + "user_auth_token/users/" + this.clientId; return resPath; } /** * Returns the resource associated with the current gspread config user authentication token. * the resource path is :- * "/repository/components/org.wso2.carbon.dataservices.core/services/[service_id]/configs/[config_id]/ * user_auth_token" */ private Resource getAuthTokenResource(Registry registry) throws Exception { if (registry == null) { return null; } String resPath = this.generateAuthTokenResourcePath(); if (!registry.resourceExists(resPath)) { return null; } return registry.get(resPath); } /** * Helper method to get current access token resides in the registry. * * @return accessToken * @throws Exception */ private String getAccessTokenFromRegistry() throws Exception { if (SQLDriverDSComponent.getRegistryService() == null) { String msg = "GSpreadConfig.getFeed(): Registry service is not available, authentication key sharing fails"; log.error(msg); throw new SQLException(msg); } Registry registry = SQLDriverDSComponent.getRegistryService() .getGovernanceSystemRegistry(TDriverUtil.getCurrentTenantId()); Resource authTokenRes = this.getAuthTokenResource(registry); if (authTokenRes != null) { Object content = authTokenRes.getContent(); if (content != null) { return new String((byte[]) content, this.charSetType); } } return null; } /** * Helper method to save new access token to registry. * * @throws Exception */ private void saveTokenToRegistry() throws Exception { if (SQLDriverDSComponent.getRegistryService() == null) { String msg = "GSpreadConfig.getFeed(): Registry service is not available, authentication key cannot be" + " saved"; log.error(msg); throw new SQLException(msg); } Registry registry = SQLDriverDSComponent.getRegistryService() .getGovernanceSystemRegistry(TDriverUtil.getCurrentTenantId()); registry.beginTransaction(); Resource res = registry.newResource(); res.setContent(this.accessToken.getBytes(this.charSetType)); registry.put(this.generateAuthTokenResourcePath(), res); registry.commitTransaction(); } public URL getSpreadSheetFeedUrl() throws MalformedURLException { return new URL(Constants.SPREADSHEET_FEED_BASE_URL + this.visibility + "/full"); } public URL generateWorksheetFeedURL(String key) throws MalformedURLException { return new URL(Constants.BASE_WORKSHEET_URL + key + "/" + this.visibility + "/basic"); } /** * method to check whether authentication is required or not * * @return true if authentication is required else false */ public boolean requiresAuth() { return (this.visibility != null && this.visibility.equals(Constants.ACCESS_MODE_PRIVATE)); } private boolean checkVisibility() { return (Constants.ACCESS_MODE_PRIVATE.equals(this.visibility) || Constants.ACCESS_MODE_PUBLIC.equals(this.visibility)); } }