/*******************************************************************************
* Copyright (c) 2010, 2014 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.server.tests.servlets.files;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.orion.internal.server.servlets.Slug;
import org.eclipse.orion.server.core.IOUtilities;
import org.eclipse.orion.server.core.LogHelper;
import org.eclipse.orion.server.core.OrionConfiguration;
import org.eclipse.orion.server.core.ProtocolConstants;
import org.eclipse.orion.server.core.metastore.ProjectInfo;
import org.eclipse.orion.server.tests.AbstractServerTest;
import org.eclipse.orion.server.tests.ServerTestsActivator;
import org.eclipse.orion.server.tests.servlets.internal.DeleteMethodWebRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xml.sax.SAXException;
import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.PostMethodWebRequest;
import com.meterware.httpunit.PutMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
/**
* Common base class for file system tests.
*/
public abstract class FileSystemTest extends AbstractServerTest {
public static final String FILE_SERVLET_LOCATION = "file/";
private static String FILESTORE_PREFIX;
/**
* Location of the test project within the file servlet namespace.
*/
protected String testProjectBaseLocation = "";
/**
* The local file system location of the test project.
*/
protected String testProjectLocalFileLocation = "";
/**
* The workspace location for the test user.
*/
protected URI workspaceLocation = null;
protected WebConversation webConversation;
/**
* Creates a test project with the given name.
* @throws SAXException
* @throws IOException
*/
protected void createTestProject(String name) throws Exception {
assertNotNull(workspaceLocation);
//create a project
String projectName = name.concat("Project");
WebRequest request = getCreateProjectRequest(workspaceLocation, projectName, null);
WebResponse response = webConversation.getResponse(request);
if (response.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
String msg = "Unexpected " + response.getResponseCode() + " response creating project " + projectName + ": " + response.getText();
System.out.println(msg);
LogHelper.log(new Status(IStatus.ERROR, ServerTestsActivator.PI_TESTS, msg));
fail(msg);
}
IPath workspacePath = new Path(workspaceLocation.getPath());
String workspaceId = new Path(workspaceLocation.getPath()).segment(workspacePath.segmentCount() - 1);
testProjectBaseLocation = "/" + workspaceId + '/' + projectName;
JSONObject project = new JSONObject(response.getText());
testProjectLocalFileLocation = "/" + project.optString(ProtocolConstants.KEY_ID, null);
}
protected boolean checkDirectoryExists(String path) throws CoreException {
IFileStore dir = EFS.getStore(makeLocalPathAbsolute(path));
return (dir.fetchInfo().exists() && dir.fetchInfo().isDirectory());
}
protected boolean checkFileExists(String path) throws CoreException {
IFileStore file = EFS.getStore(makeLocalPathAbsolute(path));
return (file.fetchInfo().exists() && !file.fetchInfo().isDirectory());
}
protected boolean checkContentEquals(File expected, String actual) throws IOException, CoreException {
File actualContent = EFS.getStore(makeLocalPathAbsolute(actual)).toLocalFile(EFS.NONE, null);
return FileUtils.contentEquals(expected, actualContent);
}
/**
* Allows a subclass test to insert a different base location for the file system contents
* of that test. The return value should be an empty string, or a path with leading
* slash and no trailing slash.
*/
protected String getTestBaseFileSystemLocation() {
return testProjectLocalFileLocation;
}
/**
* Allows a subclass test to insert a different base resource URI, such as the
* workspace/project for that test. The return value should be an empty string, or a path with leading
* slash and no trailing slash.
*/
protected String getTestBaseResourceURILocation() {
return testProjectBaseLocation;
}
protected static File getWorkspaceRoot() throws CoreException {
return EFS.getStore(URI.create(FILESTORE_PREFIX)).toLocalFile(EFS.NONE, null);
}
/**
* Creates a new directory in the server's local file system at the root location for the file servlet.
*/
protected IFileStore createDirectory(String path) throws CoreException {
IFileInfo info = null;
URI location = makeLocalPathAbsolute(path);
IFileStore dir = EFS.getStore(location);
dir.mkdir(EFS.NONE, null);
info = dir.fetchInfo();
assertTrue("Coudn't create directory " + path, info.exists() && info.isDirectory());
return dir;
}
protected static IFileStore createFile(URI uri, String fileContent) throws CoreException {
IFileStore outputFile = EFS.getStore(uri);
outputFile.delete(EFS.NONE, null);
InputStream input = new ByteArrayInputStream(fileContent.getBytes());
transferData(input, outputFile.openOutputStream(EFS.NONE, null));
IFileInfo info = outputFile.fetchInfo();
assertTrue("Coudn't create file " + uri, info.exists() && !info.isDirectory());
return outputFile;
}
protected IFileStore createFile(String path, String fileContent) throws CoreException {
return createFile(makeLocalPathAbsolute(path), fileContent);
}
/**
* Returns an absolute resource URI path within the test project for the given project
* relative path. Returns <code>null</code> if there is no test project configured.
*/
private String getAbsolutePath(String path) {
try {
String baseLocation = getTestBaseResourceURILocation();
Path basePath = new Path(baseLocation);
if (basePath.segmentCount() < 2)
return null;
String workspaceId = basePath.segment(0);
String projectName = basePath.segment(1);
ProjectInfo projectInfo = OrionConfiguration.getMetaStore().readProject(workspaceId, projectName);
if (projectInfo == null) {
return null;
}
IFileStore projectStore = OrionConfiguration.getMetaStore().getDefaultContentLocation(projectInfo);
String encodedProjectRoot = projectStore.toURI().toString() + "/";
String projectRoot = URLDecoder.decode(encodedProjectRoot, "UTF-8");
String absolutePath = new Path(projectRoot).append(path).toString();
return absolutePath;
} catch (CoreException e) {
fail(e.getLocalizedMessage());
} catch (UnsupportedEncodingException e) {
fail(e.getLocalizedMessage());
}
return null;
}
protected URI makeLocalPathAbsolute(String path) {
String absolutePath = getAbsolutePath(path);
try {
return URIUtil.fromString(absolutePath);
} catch (URISyntaxException e) {
fail(e.getMessage());
return null;
}
}
protected static void initializeWorkspaceLocation() {
IFileStore root = OrionConfiguration.getRootLocation();
FILESTORE_PREFIX = root.toURI().toString() + "/";
}
protected void remove(String path) throws CoreException {
String absolutePath = getAbsolutePath(path);
if (absolutePath != null) {
IFileStore outputFile = EFS.getStore(URI.create(absolutePath));
outputFile.delete(EFS.NONE, null);
}
}
private static void transferData(InputStream input, OutputStream output) {
try {
try {
int c = 0;
while ((c = input.read()) != -1)
output.write(c);
} finally {
input.close();
output.close();
}
} catch (IOException e) {
e.printStackTrace();
assertTrue(e.toString(), false);
}
}
protected void checkDirectoryMetadata(JSONObject dirObject, String name, String path, Long localTimestamp, Boolean isReadonly, Boolean isExecutable, String location) throws JSONException {
assertTrue("Expected directory but found file", dirObject.getBoolean(ProtocolConstants.KEY_DIRECTORY));
if (name != null) {
assertEquals("Invalid directory name", name, dirObject.getString(ProtocolConstants.KEY_NAME));
}
if (path != null) {
assertEquals("Invalid directory path", path, dirObject.getString(ProtocolConstants.KEY_PATH));
}
if (localTimestamp != null) {
if (localTimestamp < 0) {
assertNotNull("Directory timestamp should not be null", dirObject.getLong(ProtocolConstants.KEY_LOCAL_TIMESTAMP));
} else {
assertEquals("Invalid directory timestamp", localTimestamp, new Long(dirObject.getLong(ProtocolConstants.KEY_LOCAL_TIMESTAMP)));
}
}
if (location != null) {
assertEquals("Invalid directory location", location, dirObject.getString(ProtocolConstants.KEY_LOCATION));
}
if (isReadonly != null && isExecutable != null) {
JSONObject attributes = dirObject.getJSONObject(ProtocolConstants.KEY_ATTRIBUTES);
assertNotNull("Expected Attributes section in directory metadata", attributes);
assertEquals("Ibvalid directory readonly attribute", isReadonly, attributes.getBoolean(ProtocolConstants.KEY_ATTRIBUTE_READ_ONLY));
assertEquals("Invalid directory executable attribute", isExecutable, attributes.getBoolean(ProtocolConstants.KEY_ATTRIBUTE_EXECUTABLE));
}
}
protected void checkFileMetadata(JSONObject fileObject, String name, Long localTimestamp, String charset, String contentType, String location, Long length, Boolean isReadonly, Boolean isExecutable, String parentName) throws JSONException {
assertFalse("Expected file but found directory", fileObject.getBoolean(ProtocolConstants.KEY_DIRECTORY));
if (name != null) {
assertEquals("Invalid file name", name, fileObject.getString(ProtocolConstants.KEY_NAME));
}
if (localTimestamp != null) {
if (localTimestamp < 0) {
assertNotNull("File timestamp should not be null", fileObject.getLong(ProtocolConstants.KEY_LOCAL_TIMESTAMP));
} else {
assertEquals("Invalid file timestamp", localTimestamp, new Long(fileObject.getLong(ProtocolConstants.KEY_LOCAL_TIMESTAMP)));
}
}
if (charset != null) {
assertEquals("Invalid file charset", charset, fileObject.getString("Charset"));
}
if (contentType != null) {
assertEquals("Invalid file content type", contentType, fileObject.getString("ContentType"));
}
if (location != null) {
assertEquals("Invalid file location", location, fileObject.getString(ProtocolConstants.KEY_LOCATION));
}
if (length != null) {
assertEquals("Invalid file length", length, new Long(fileObject.getLong(ProtocolConstants.KEY_LENGTH)));
}
if (isReadonly != null || isExecutable != null) {
int attrs = EFS.getLocalFileSystem().attributes();
JSONObject attributes = fileObject.optJSONObject(ProtocolConstants.KEY_ATTRIBUTES);
assertNotNull("Expected Attributes section in file metadata", attributes);
if (isReadonly != null && ((attrs & EFS.ATTRIBUTE_READ_ONLY) != 0)) {
assertEquals("Invalid file readonly attribute", isReadonly.booleanValue(), attributes.getBoolean(ProtocolConstants.KEY_ATTRIBUTE_READ_ONLY));
}
if (isExecutable != null && ((attrs & EFS.ATTRIBUTE_EXECUTABLE) != 0)) {
assertEquals("Invalid file executable attribute", isExecutable.booleanValue(), attributes.getBoolean(ProtocolConstants.KEY_ATTRIBUTE_EXECUTABLE));
}
}
if (parentName != null) {
JSONArray parents = fileObject.getJSONArray(ProtocolConstants.KEY_PARENTS);
assertNotNull("Expected Parents array in file metadata", parents);
assertEquals("Expected single parent item", 1, parents.length());
JSONObject parent = parents.getJSONObject(0);
assertNotNull("Invalid parent item", parent);
assertEquals("Invalid file parent name", parentName, parent.get(ProtocolConstants.KEY_NAME));
}
}
protected WebRequest getDeleteFilesRequest(String uri) {
WebRequest request = new DeleteMethodWebRequest(makeResourceURIAbsolute(uri));
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
}
protected List<JSONObject> getDirectoryChildren(JSONObject dirObject) throws JSONException {
assertTrue("Expected directory but found file", dirObject.getBoolean(ProtocolConstants.KEY_DIRECTORY));
List<JSONObject> children = new ArrayList<JSONObject>();
try {
JSONArray chidrenArray = dirObject.getJSONArray(ProtocolConstants.KEY_CHILDREN);
for (int i = 0; i < chidrenArray.length(); i++) {
children.add(chidrenArray.getJSONObject(i));
}
} catch (JSONException e) {
// when no Children section attached return empty list
}
return children;
}
/**
* Creates a request to get the resource at the given location.
* @param location Either an absolute URI, or a workspace-relative URI
*/
protected WebRequest getGetFilesRequest(String location) {
String requestURI = makeResourceURIAbsolute(location);
WebRequest request = new GetMethodWebRequest(requestURI);
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
}
protected JSONObject getNewDirJSON(String dirName) throws JSONException {
JSONObject json = new JSONObject();
json.put(ProtocolConstants.KEY_NAME, dirName);
json.put(ProtocolConstants.KEY_DIRECTORY, true);
return json;
}
protected JSONObject getNewFileJSON(String dirName) throws JSONException {
JSONObject json = new JSONObject();
if (dirName != null)
json.put(ProtocolConstants.KEY_NAME, dirName);
json.put(ProtocolConstants.KEY_DIRECTORY, false);
json.put("Charset", "UTF-8");
json.put("ContentType", "text/plain");
return json;
}
protected WebRequest getPatchFileRequest(String uri, String json) {
try {
WebRequest request = new PostMethodWebRequest(makeResourceURIAbsolute(uri), IOUtilities.toInputStream(json), "application/json");
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
request.setHeaderField(ProtocolConstants.HEADER_METHOD_OVERRIDE, "PATCH");
setAuthentication(request);
return request;
} catch (UnsupportedEncodingException e) {
fail(e.getMessage());
}
//Java is cool
return null;
}
protected WebRequest getPostFilesRequest(String uri, String json, String slug) {
try {
WebRequest request = new PostMethodWebRequest(makeResourceURIAbsolute(uri), IOUtilities.toInputStream(json), "application/json");
request.setHeaderField(ProtocolConstants.HEADER_SLUG, Slug.encode(slug));
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
} catch (UnsupportedEncodingException e) {
fail(e.getMessage());
}
//can never get here
return null;
}
protected WebRequest getPutFileRequest(String uri, String body) {
try {
WebRequest request = new PutMethodWebRequest(makeResourceURIAbsolute(uri), IOUtilities.toInputStream(body), "application/json");
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
} catch (UnsupportedEncodingException e) {
fail(e.getMessage());
}
//can never get here
return null;
}
protected WebRequest getPutFileRequest(String uri, byte[] body) {
ByteArrayInputStream source = new ByteArrayInputStream(body);
WebRequest request = new PutMethodWebRequest(makeResourceURIAbsolute(uri), source, "application/json");
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
}
/**
* Makes a URI absolute. If the provided URI is relative, it is assumed to be relative to the workspace location (file servlet location).
* If the provided URI is already absolute it is returned as-is.
* The provided URI must be correctly encoded.
*/
protected String makeResourceURIAbsolute(String uriString) {
try {
URI uri = new URI(uriString);
if (uri.isAbsolute())
return uriString;
if (uriString.startsWith("/")) {
return toAbsoluteURI(uriString);
}
} catch (URISyntaxException e) {
//unencoded string - fall through
}
try {
if (uriString.startsWith(FILE_SERVLET_LOCATION))
return URIUtil.fromString(SERVER_LOCATION + uriString).toString();
//avoid double slash
if (uriString.startsWith("/"))
uriString = uriString.substring(1);
String path = new Path(FILE_SERVLET_LOCATION).append(getTestBaseResourceURILocation()).append(uriString).toString();
return new URI(SERVER_LOCATION + path).toString();
} catch (URISyntaxException e) {
fail(e.getMessage());
return null;
}
}
protected WebRequest getCreateWorkspaceRequest(String workspaceName) {
WebRequest request = new PostMethodWebRequest(SERVER_LOCATION + "/workspace");
if (workspaceName != null)
request.setHeaderField(ProtocolConstants.HEADER_SLUG, workspaceName);
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
}
protected WebRequest getCreateProjectRequest(URI workspaceLocationURI, String projectName, String projectLocation) throws JSONException, IOException {
workspaceLocationURI = addSchemeHostPort(workspaceLocationURI);
JSONObject body = new JSONObject();
if (projectLocation != null)
body.put(ProtocolConstants.KEY_CONTENT_LOCATION, projectLocation);
InputStream in = IOUtilities.toInputStream(body.toString());
WebRequest request = new PostMethodWebRequest(workspaceLocationURI.toString(), in, "UTF-8");
if (projectName != null)
request.setHeaderField(ProtocolConstants.HEADER_SLUG, projectName);
request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
setAuthentication(request);
return request;
}
protected URI addSchemeHostPort(URI uri) {
String scheme = uri.getScheme();
String host = uri.getHost();
int port = uri.getPort();
if (scheme == null) {
scheme = "http";
}
if (host == null) {
host = "localhost";
}
if (port == -1) {
port = 8080;
}
try {
return new URI(scheme, uri.getUserInfo(), host, port, uri.getPath(), uri.getQuery(), uri.getFragment());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
/**
* Creates a new workspace, and returns the raw response object.
*/
protected WebResponse basicCreateWorkspace(String workspaceName) throws IOException, SAXException {
WebRequest request = getCreateWorkspaceRequest(workspaceName);
WebResponse response = webConversation.getResponse(request);
if (response.getResponseCode() != HttpURLConnection.HTTP_OK) {
System.err.println(response.getResponseMessage());
assertEquals("Unexpected failure creating workspace with name: " + workspaceName, HttpURLConnection.HTTP_OK, response.getResponseCode());
}
return response;
}
/**
* Creates a new workspace, and returns the URI of the resulting resource.
*/
protected void createWorkspace(String workspaceName) throws IOException, SAXException {
WebResponse response = basicCreateWorkspace(workspaceName);
workspaceLocation = SERVER_URI.resolve(response.getHeaderField(ProtocolConstants.HEADER_LOCATION));
}
}