/******************************************************************************* * Copyright (c) 2014, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.internal.server.servlets.workspace; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.io.DirectoryWalker; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Path; import org.eclipse.orion.server.core.OrionConfiguration; import org.eclipse.orion.server.core.ProtocolConstants; import org.eclipse.orion.server.core.metastore.WorkspaceInfo; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * Handles the creation of the file list for a users workspace. See Bugzilla 453114. * * @author Anthony Hunter * */ public class FileListDirectoryWalker extends DirectoryWalker<File> { /** * The root of a users workspace */ private File workspaceRoot; private boolean isRoot = true; /** * The path of the workspace root used to create the path for the file. */ private String workspacePath; /** * The length of the absolute path of the workspace file. */ private int workspaceRootSuffixLength; private String filter; private IFileStore workspaceHome; static final String SYNC_VERSION = "1.0"; public FileListDirectoryWalker(WorkspaceInfo workspaceInfo) { this(workspaceInfo, null); } public FileListDirectoryWalker(WorkspaceInfo workspaceInfo, String filterPath) { init(workspaceInfo, filterPath); } private void init(WorkspaceInfo workspaceInfo, String filterPath) { String workspaceId = workspaceInfo.getUniqueId(); filter = filterPath; IFileStore userHome = OrionConfiguration.getMetaStore().getUserHome(workspaceInfo.getUserId()); workspaceHome = userHome.getChild(workspaceId.substring(workspaceId.indexOf('-') + 1)); try { workspaceRoot = workspaceHome.toLocalFile(EFS.NONE, null); } catch (CoreException e) { // should never happen throw new RuntimeException(e); } workspacePath = "/file/" + workspaceId; workspaceRootSuffixLength = workspaceRoot.getAbsolutePath().length(); } @Override protected void handleFile(final File file, final int depth, final Collection<File> results) throws IOException { results.add(file); } @Override protected boolean handleDirectory(File directory, int depth, Collection<File> results) throws IOException { if (!isRoot) { results.add(directory); } else { isRoot = false; } return true; } public JSONObject getFileList() { List<File> files = new ArrayList<File>(); try { if (filter != null) { IFileStore projHome = workspaceHome.getChild(filter); try { File projRoot = projHome.toLocalFile(EFS.NONE, null); walk(projRoot, files); } catch (CoreException e) { // should never happen throw new RuntimeException(e); } } else { walk(workspaceRoot, files); } } catch (IOException e) { // should never happen throw new RuntimeException(e); } JSONObject json = new JSONObject(); try { JSONArray jsonArray = new JSONArray(); for (File file : files) { jsonArray.put(toJSON(file, workspaceRootSuffixLength)); } json.put("FileList", jsonArray); json.put("Timestamp", System.currentTimeMillis()); json.put("SyncVersion", FileListDirectoryWalker.SYNC_VERSION); } catch (JSONException e) { // should never happen throw new RuntimeException(e); } return json; } private JSONObject toJSON(File file, int workspaceRootSuffixLength) throws JSONException { JSONObject json = new JSONObject(); String filePath = file.getAbsolutePath().substring(workspaceRootSuffixLength); filePath = new Path(filePath).toPortableString(); if (file.isFile()) { String sha = checkSum(file.getAbsolutePath()); json.put("SHA", sha); } else { if (!filePath.endsWith("/")) { filePath += "/"; } } json.put(ProtocolConstants.KEY_LENGTH, file.isFile() ? Long.toString(file.length()) : file.list().length); try { json.put(ProtocolConstants.KEY_LOCATION, new URI("orion", null, workspacePath + filePath, null, null)); json.put(ProtocolConstants.KEY_LAST_MODIFIED, Long.toString(file.lastModified())); } catch (URISyntaxException e) { // should never happen throw new RuntimeException(e); } return json; } String checkSum(String path) { FileInputStream stream = null; try { stream = new FileInputStream(path); MessageDigest digest = MessageDigest.getInstance("SHA-1"); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = stream.read(buffer)) > 0) { digest.update(buffer, 0, bytesRead); } byte[] hashedBytes = digest.digest(); return convertByteArrayToHexString(hashedBytes); } catch (Exception e) { } finally { if (stream != null) try { stream.close(); } catch (IOException e) { } } return ""; } private static String convertByteArrayToHexString(byte[] arrayBytes) { StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < arrayBytes.length; i++) { stringBuffer.append(Integer.toString((arrayBytes[i] & 0xff) + 0x100, 16).substring(1)); } return stringBuffer.toString(); } }