Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.googlecode.osde.internal.igoogle; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.googlecode.osde.internal.builders.GadgetBuilder; import com.googlecode.osde.internal.utils.Logger; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.FileEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.eclipse.core.resources.IProject; /** * This iGoogle utility class provides hosting-related methods to * interact with iGoogle (http://www.google.com/ig) gadget container. * * @author albert.cheng.ig@gmail.com */ public class IgHostingUtil { private static Logger logger = new Logger(IgHostingUtil.class); private static final String HTTP_PLAIN_TEXT_TYPE = HTTP.PLAIN_TEXT_TYPE + HTTP.CHARSET_PARAM + IgHttpUtil.ENCODING; private static final String URL_IG_GADGETS_BYTE_QUOTA = IgHttpUtil.URL_IG_GADGETS + "/bytequota/"; private static final String URL_IG_GADGETS_BYTES_USED = IgHttpUtil.URL_IG_GADGETS + "/bytesused/"; private static final String URL_IG_GADGETS_DIRECTORY = IgHttpUtil.URL_IG_GADGETS + "/directory/"; private static final String URL_IG_GADGETS_FILE = IgHttpUtil.URL_IG_GADGETS + "/file/"; private static final String URL_IG_PREVIEW_OPEN_SOCIAL_GADGET = IgHttpUtil.URL_HTTP_IG + "/iframeurl?moduleurl="; private static final String URL_IG_SUBMIT_GADGET = IgHttpUtil.URL_HTTP_IG + "/submit?prefill_url="; private static final String URL_GMODULE_FILE = "http://hosting.gmodules.com/ig/gadgets/file/"; private static final String URL_PREVIEW_LEGACY_GADGET = "http://www.gmodules.com/gadgets/ifr?nocache=1"; private static final String URL_ADD_GADGET = "http://www.google.com/ig/directory?url="; private static final String PREVIEW_URL_IDENTIFIER_FOR_HOME = "name:\"home\",ifr:\""; private static final String PREVIEW_URL_IDENTIFIER_FOR_CANVAS = "name:\"canvas\",ifr:\""; private static final String PREVIEW_URL_END_IDENTIFIER = "&is_social=1"; private static final int PREVIEW_URL_END_IDENTIFIER_LENGTH = PREVIEW_URL_END_IDENTIFIER.length(); private IgHostingUtil() { // Disable instantiation of this utility class. } /** * Uploads files to iGoogle. * * @return the relative file paths for the hosted files * @throws IgException */ static List<String> uploadFiles(IgCredentials igCredentials, IProject project, String hostingFolder) throws IgException { String uploadFromPath = project.getFolder(GadgetBuilder.TARGET_FOLDER_NAME).getLocation().toOSString(); logger.fine("uploadFromPath: " + uploadFromPath); // Upload files. List<String> relativeFilePaths = IgHostingUtil.uploadFiles(igCredentials, uploadFromPath, hostingFolder); return relativeFilePaths; } /** * Uploads files to iGoogle. * * @return the relative file paths for the hosted files * @throws IgException */ public static List<String> uploadFiles(IgCredentials igCredentials, String sourceFileRootPath, String hostingFolder) throws IgException { List<String> relativeFilePaths = findAllRelativeFilePaths(sourceFileRootPath); for (String relativePath : relativeFilePaths) { logger.fine("uploading file: " + relativePath); uploadFile(igCredentials, sourceFileRootPath, relativePath, hostingFolder); } return relativeFilePaths; } /** * Uploads a file to iGoogle. * * @throws IgException */ public static void uploadFile(IgCredentials igCredentials, String sourceFileRootPath, String sourceFileRelativePath, String hostingFolder) throws IgException { // Prepare HttpPost. String httpPostUrl = URL_IG_GADGETS_FILE + igCredentials.getPublicId() + hostingFolder + sourceFileRelativePath + "?et=" + igCredentials.getEditToken(); logger.fine("httpPostUrl: " + httpPostUrl); HttpPost httpPost = new HttpPost(httpPostUrl); File sourceFile = new File(sourceFileRootPath, sourceFileRelativePath); String httpContentType = isTextExtensionForGadgetFile(sourceFileRelativePath) ? HTTP_PLAIN_TEXT_TYPE : HTTP.DEFAULT_CONTENT_TYPE; httpPost.setHeader(HTTP.CONTENT_TYPE, httpContentType); FileEntity fileEntity = new FileEntity(sourceFile, httpContentType); logger.fine("fileEntity length: " + fileEntity.getContentLength()); httpPost.setEntity(fileEntity); // Add cookie headers. Cookie PREF must be placed before SID. httpPost.addHeader(IgHttpUtil.HTTP_HEADER_COOKIE, "PREF=" + igCredentials.getPref()); httpPost.addHeader(IgHttpUtil.HTTP_HEADER_COOKIE, "SID=" + igCredentials.getSid()); // Execute request. HttpClient httpClient = new DefaultHttpClient(); HttpResponse httpResponse; try { httpResponse = httpClient.execute(httpPost); } catch (ClientProtocolException e) { throw new IgException(e); } catch (IOException e) { throw new IgException(e); } // Verify if the file is created. StatusLine statusLine = httpResponse.getStatusLine(); logger.fine("statusLine: " + statusLine); if (HttpStatus.SC_CREATED != statusLine.getStatusCode()) { String response = IgHttpUtil.retrieveHttpResponseAsString(httpClient, httpPost, httpResponse); throw new IgException("Failed file-upload with status line: " + statusLine.toString() + ",\nand response:\n" + response); } } private static boolean isTextExtensionForGadgetFile(String fileName) { int lastIndexOfDot = fileName.lastIndexOf("."); // Return false if no dot found or the last char is dot. if ((lastIndexOfDot == -1) || (lastIndexOfDot == (fileName.length() - 1))) { return false; } // The following extensions in textExtensions are treated as text types. // It is ok to treat all the other extensions as default types. String[] textExtensions = new String[] { "xml", "js", "css", "html", "htm" }; String extension = fileName.substring(lastIndexOfDot + 1).toLowerCase(); for (String textExtension : textExtensions) { if (textExtension.equals(extension)) { return true; } } return false; } /** * Finds all relative file paths under given base folder. * These files will be uploaded to iGoogle server. */ static List<String> findAllRelativeFilePaths(String baseFolder) { List<String> allPaths = new ArrayList<String>(); findAllRelativeFilePaths(baseFolder, "", allPaths); return allPaths; } private static void findAllRelativeFilePaths(String baseFolder, String relativeFolder, List<String> allPaths) { // Assert that relativeFolder ends with "/" unless it is empty. assert ((relativeFolder.length() == 0) || (relativeFolder.charAt(relativeFolder.length() - 1) == '/')); File currentFolder = new File(baseFolder, relativeFolder); for (File file : currentFolder.listFiles()) { String relativeFilePath = relativeFolder + file.getName(); if (file.isHidden()) { // Ignore any hidden file. } else if (file.isDirectory()) { findAllRelativeFilePaths(baseFolder, relativeFilePath + "/", allPaths); } else if (file.isFile()) { allPaths.add(relativeFilePath); } // Ignore all other kinds of files. } } static String retrieveQuotaByte(String sid, String publicId) throws IgException { String url = URL_IG_GADGETS_BYTE_QUOTA + publicId; String response = retrieveHttpResponseAsString(url, sid); return response; } static String retrieveQuotaByteUsed(String sid, String publicId) throws IgException { String url = URL_IG_GADGETS_BYTES_USED + publicId; String response = retrieveHttpResponseAsString(url, sid); return response; } /** * Retrieves list of file names under hosting folder. * Returns empty list if no file is found. * * @throws IgException */ static List<String> retrieveFileNameList(String sid, String publicId, String hostingFolder) throws IgException { String fileListUrl = URL_IG_GADGETS_DIRECTORY + publicId; logger.fine("fileListUrl: " + fileListUrl); String allFilesString = retrieveHttpResponseAsString(fileListUrl, sid); logger.fine("allFilesString:\n" + allFilesString); List<String> allFileNameList = new ArrayList<String>(); // Return empty List when no file is found. if (allFilesString.indexOf("<title>404 Not Found</title>") != -1) { return allFileNameList; } // Form List of files. String[] allFilesArray = allFilesString.split("\\n"); // Sample of details of each file (namely, each line of returned content): // "39 text/plain; charset=UTF-8 dummy_host_folder/dummy_folder/dummy_file.xml" // So, make sure the hostingFolder should not start with "/". if (hostingFolder.startsWith("/")) { hostingFolder = hostingFolder.substring(1); } for (String file : allFilesArray) { String[] fileInfo = file.split(" "); String fileName = fileInfo[fileInfo.length - 1]; if (fileName.startsWith(hostingFolder)) { allFileNameList.add(fileName); } } return allFileNameList; } /** * Returns the full url for hosting given public id and hosting * folder name. * * @param publicId iGoogle public id * @param hostingFolder hosting folder name which starts and ends with "/" * @return the full url for hosting which ends with "/" */ public static String formHostingUrl(String publicId, String hostingFolder) { return URL_GMODULE_FILE + publicId + hostingFolder; } /** * Sends HTTP request to iGoogle server and retrieves response * content as String. * * @return response as String * @throws IgException */ static String retrieveHttpResponseAsString(String url, String sid) throws IgException { // Prepare HttpGet. HttpGet httpGet = new HttpGet(url); httpGet.setHeader(HTTP.CONTENT_TYPE, HTTP.PLAIN_TEXT_TYPE); httpGet.addHeader(IgHttpUtil.HTTP_HEADER_COOKIE, "SID=" + sid); // Retrieve HttpResponse. HttpClient httpClient = new DefaultHttpClient(); HttpResponse httpResponse; try { httpResponse = httpClient.execute(httpGet); } catch (ClientProtocolException e) { throw new IgException(e); } catch (IOException e) { throw new IgException(e); } logger.fine("status line: " + httpResponse.getStatusLine()); return IgHttpUtil.retrieveHttpResponseAsString(httpClient, httpGet, httpResponse); } /** * Forms the url for adding a gadget to iGoogle. */ public static String formAddGadgetUrl(String hostedFileUrl) { StringBuilder sb = new StringBuilder(URL_ADD_GADGET); // Append hosted file's url. sb.append(hostedFileUrl); return sb.toString(); } /** * Forms the url for previewing a legacy gadget. * * @return url for previewing */ public static String formPreviewLegacyGadgetUrl(String hostedFileUrl, boolean useCanvasView) { StringBuilder sb = new StringBuilder(); sb.append(URL_PREVIEW_LEGACY_GADGET); sb.append("&hl=en&gl=us&view="); sb.append(useCanvasView ? "canvas" : "home"); // default is home view // Append hosted file's url. sb.append("&url="); sb.append(hostedFileUrl); // TODO: Support various languages, and countries. return sb.toString(); } /** * Forms url for previewing an OpenSocial gadget. * * @return url for previewing * @throws IgException */ public static String formPreviewOpenSocialGadgetUrl(String hostedFileUrl, boolean useCanvasView, String sid) throws IgException { String requestUrl = URL_IG_PREVIEW_OPEN_SOCIAL_GADGET + hostedFileUrl; logger.fine("requestUrl: " + requestUrl); String response = retrieveHttpResponseAsString(requestUrl, sid); logger.fine("response:\n" + response); // Sample response (all in one line): // throw 1; < don't be evil' > // {m:[{url:"http://.../osde/preview/gadget.xml", // view:[{ // name:"home", // ifr:"http://....ig.ig.gmodules.com/gadgets/ifr? // view=home // &url=http://.../file/...546/osde/preview/gadget.xml // &nocache=0 // &lang=en&country=us&.lang=en&.country=us // &synd=ig&mid=0&ifpctok=-1987853101061147102 // &exp_split_js=1&exp_track_js=1&exp_new_js_flags=1&exp_ids=... // &parent=http://www.google.com&refresh=3600 // &libs=core:core.io:core.iglegacy:auth-refresh#st=... // &gadgetId=...646 // &gadgetOwner=...450 // &gadgetViewer=...450 // &is_signedin=1 // &is_social=1 // "}] // }]} // Form preview-url from response. // First, take out unwanted prepending string. String previewUrlIdentifier = useCanvasView ? PREVIEW_URL_IDENTIFIER_FOR_CANVAS : PREVIEW_URL_IDENTIFIER_FOR_HOME; int startIndex = response.indexOf(previewUrlIdentifier) + previewUrlIdentifier.length(); String previewUrl = response.substring(startIndex); logger.fine("previewUrl with appending:\n" + previewUrl); // Handle the case when Canvas view is unavailable. if (!previewUrl.startsWith("http://")) { if (!useCanvasView) { throw new IgException("Error: Invalid preview url with response:\n" + response); } throw new IgException("Canvas view is not available for this gadget."); } // Take out unwanted appending string. int endIndex = previewUrl.indexOf(PREVIEW_URL_END_IDENTIFIER) + PREVIEW_URL_END_IDENTIFIER_LENGTH; previewUrl = previewUrl.substring(0, endIndex); logger.fine("previewUrl without appending:\n" + previewUrl); // Always enable nocache. previewUrl = previewUrl.replaceFirst("&nocache=0&", "&nocache=1&"); // TODO: support lang and country. return previewUrl; } /** * Forms the url for publishing a gadget. * * @return url for publishing */ public static String formPublishGadgetUrl(String hostedFileUrl) { return URL_IG_SUBMIT_GADGET + hostedFileUrl; } /** * Deletes the hosted file. */ public static void deleteFile(String fileName, IgCredentials igCredentials) throws IgException { // Make sure fileName starts with "/". if (!fileName.startsWith("/")) { fileName = "/" + fileName; } // Prepare HttpPost. String url = URL_IG_GADGETS_FILE + igCredentials.getPublicId() + fileName + "?synd=gde&action=delete&et=" + igCredentials.getEditToken(); HttpPost httpPost = new HttpPost(url); httpPost.setHeader(HTTP.CONTENT_TYPE, "application/xml; charset=" + IgHttpUtil.ENCODING); // Do not set Content-Length header. // Add cookie headers. Cookie PREF must be placed before SID. httpPost.addHeader(IgHttpUtil.HTTP_HEADER_COOKIE, "PREF=" + igCredentials.getPref()); httpPost.addHeader(IgHttpUtil.HTTP_HEADER_COOKIE, "SID=" + igCredentials.getSid()); // Execute request. HttpClient httpClient = new DefaultHttpClient(); HttpResponse httpResponse; try { httpResponse = httpClient.execute(httpPost); } catch (ClientProtocolException e) { throw new IgException(e); } catch (IOException e) { throw new IgException(e); } StatusLine statusLine = httpResponse.getStatusLine(); // Verify result status. if (HttpStatus.SC_NO_CONTENT != statusLine.getStatusCode()) { String response = IgHttpUtil.retrieveHttpResponseAsString(httpClient, httpPost, httpResponse); throw new IgException("Failed delete file with status line: " + statusLine.toString() + "\nand response:\n" + response); } } /** * Cleans all files as hosted under the given hosting folder in iGoogle. * * @throws IgException */ public static void cleanFiles(IgCredentials igCredentials, String hostingFolder) throws IgException { List<String> hostedFilesList = retrieveFileNameList(igCredentials.getSid(), igCredentials.getPublicId(), hostingFolder); logger.fine("file count: " + hostedFilesList.size()); for (String file : hostedFilesList) { deleteFile(file, igCredentials); } } }