Java tutorial
/*! * Copyright 2002 - 2013 Webdetails, a Pentaho company. All rights reserved. * * This software was developed by Webdetails and is provided under the terms * of the Mozilla Public License, Version 2.0, or any later version. You may not use * this file except in compliance with the license. If you need a copy of the license, * please go to http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails. * * Software distributed under the Mozilla Public License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to * the license for the specific language governing your rights and limitations. */ package pt.webdetails.cfr; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.TreeSet; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.pentaho.platform.api.engine.IParameterProvider; import pt.webdetails.cfr.auth.FilePermissionEnum; import pt.webdetails.cfr.auth.FilePermissionMetadata; import pt.webdetails.cfr.file.CfrFile; import pt.webdetails.cfr.file.FileStorer; import pt.webdetails.cfr.file.IFile; import pt.webdetails.cfr.file.MetadataReader; import pt.webdetails.cfr.repository.IFileRepository; import pt.webdetails.cpf.InterPluginCall; import pt.webdetails.cpf.InvalidOperationException; import pt.webdetails.cpf.SimpleContentGenerator; import pt.webdetails.cpf.WrapperUtils; import pt.webdetails.cpf.annotations.AccessLevel; import pt.webdetails.cpf.annotations.Exposed; import pt.webdetails.cpf.persistence.PersistenceEngine; import pt.webdetails.cpf.utils.CharsetHelper; import pt.webdetails.cpf.utils.MimeTypes; import pt.webdetails.cpf.utils.PluginUtils; import pt.webdetails.cpf.VersionChecker; public class CfrContentGenerator extends SimpleContentGenerator { private static final long serialVersionUID = 1L; private static Map<String, Method> exposedMethods = new HashMap<String, Method>(); private CfrService service = new CfrService(); private MetadataReader mr = new MetadataReader(service); private static final String UI_PATH = "cfr/presentation/"; static { // to keep case-insensitive methods exposedMethods = getExposedMethods(CfrContentGenerator.class, true); } static String checkRelativePathSanity(String path) { String result = path; if (path != null) { if (result.startsWith("./")) { result = result.replaceFirst("./", ""); } if (result.startsWith(".")) { result = result.replaceFirst(".", ""); } if (result.startsWith("/")) { result = result.replaceFirst(".", ""); } if (result.endsWith("/")) { result = result.substring(0, result.length() - 1); } } return result; } static String relativeFilePath(String baseDir, String file) { String _baseDir = checkRelativePathSanity(baseDir); String _file = checkRelativePathSanity(file); String result = null; if (_baseDir == null || _baseDir.length() == 0) { return _file; } else { if (_baseDir.endsWith("/")) { result = new StringBuilder(_baseDir).append(_file).toString(); } else { result = new StringBuilder(_baseDir).append('/').append(_file).toString(); } } return result; } @Override protected Method getMethod(String methodName) throws NoSuchMethodException { Method method = exposedMethods.get(StringUtils.lowerCase(methodName)); if (method == null) { throw new NoSuchMethodException(); } return method; } @Override public String getPluginName() { return "cfr"; } @Exposed(accessLevel = AccessLevel.PUBLIC) public void home(OutputStream out) throws IOException { IParameterProvider requestParams = getRequestParameters(); // IParameterProvider pathParams = getPathParameters(); ServletRequest wrapper = getRequest(); String root = wrapper.getScheme() + "://" + wrapper.getServerName() + ":" + wrapper.getServerPort(); Map<String, Object> params = new HashMap<String, Object>(); params.put("solution", "system"); params.put("path", "cfr/presentation/"); params.put("file", "cfr.wcdf"); params.put("absolute", "false"); params.put("inferScheme", "false"); params.put("root", root); // add request parameters PluginUtils.copyParametersFromProvider(params, WrapperUtils.wrapParamProvider(requestParams)); if (requestParams.hasParameter("mode") && requestParams.getStringParameter("mode", "Render").equals("edit")) { // Send this to CDE redirectToCdeEditor(out, params); return; } InterPluginCall pluginCall = new InterPluginCall(InterPluginCall.CDE, "Render", params); pluginCall.setResponse(getResponse()); pluginCall.setRequest(getRequest()); pluginCall.setOutputStream(out); pluginCall.run(); } @Exposed(accessLevel = AccessLevel.PUBLIC) public void createFolder(OutputStream out) throws Exception { String path = checkRelativePathSanity(getRequestParameters().getStringParameter("path", "")); if (path == null || StringUtils.isBlank(path)) { throw new Exception("path is null or empty"); } boolean createResult = service.getRepository().createFolder(path); writeOut(out, new JSONObject().put("result", createResult).toString()); } @Exposed(accessLevel = AccessLevel.PUBLIC) public void remove(OutputStream out) throws Exception { String fullFileName = checkRelativePathSanity(getRequestParameters().getStringParameter("fileName", null)); if (fullFileName == null || StringUtils.isBlank(fullFileName)) { throw new Exception("fileName is null or empty"); } boolean removeResult = service.getRepository().deleteFile(fullFileName); boolean result = false; if (removeResult) { FileStorer.deletePermissions(fullFileName, null); result = FileStorer.removeFile(fullFileName, null); } writeOut(out, new JSONObject().put("result", result).toString()); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void store(OutputStream out) throws IOException, InvalidOperationException, Exception { FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List /* FileItem */ items = upload.parseRequest(getRequest()); String fileName = null, savePath = null; byte[] contents = null; for (int i = 0; i < items.size(); i++) { FileItem fi = (FileItem) items.get(i); if ("path".equals(fi.getFieldName())) { savePath = fi.getString(); } if ("file".equals(fi.getFieldName())) { contents = fi.get(); fileName = fi.getName(); } } if (fileName == null) { logger.error("parameter fileName must not be null"); throw new Exception("paramete fileName must not be null"); } if (savePath == null) { logger.error("parameter path must not be null"); throw new Exception("parameter path must not be null"); } if (contents == null) { logger.error("File content must not be null"); throw new Exception("File content must not be null"); } FileStorer fileStorer = new FileStorer(service.getRepository()); boolean stored = fileStorer.storeFile(checkRelativePathSanity(fileName), checkRelativePathSanity(savePath), contents, userSession.getName()); JSONObject result = new JSONObject().put("result", stored); writeOut(out, result.toString()); } @Exposed(accessLevel = AccessLevel.PUBLIC) public void listFiles(OutputStream out) throws IOException { String baseDir = URLDecoder.decode(getRequestParameters().getStringParameter("dir", ""), "ISO-8859-1"); IFile[] files = service.getRepository().listFiles(baseDir); List<IFile> allowedFiles = new ArrayList<IFile>(files.length); String extensions = getRequestParameters().getStringParameter("fileExtensions", ""); // checks permissions /* * remarks: ideally the repository must list only the files that the current user is allowed to access? */ for (IFile file : files) { String relativePath = relativeFilePath(baseDir, file.getName()); if (mr.isCurrentUserAllowed(FilePermissionEnum.READ, relativePath)) { allowedFiles.add(file); } } String[] exts = null; if (!StringUtils.isBlank(extensions)) { exts = extensions.split(" "); } IFile[] allowedFilesArray = new IFile[allowedFiles.size()]; writeOut(out, toJQueryFileTree(baseDir, allowedFiles.toArray(allowedFilesArray), exts)); } @Exposed(accessLevel = AccessLevel.PUBLIC) public void getFile(OutputStream out) throws IOException, JSONException, Exception { String fullFileName = checkRelativePathSanity(getRequestParameters().getStringParameter("fileName", null)); if (fullFileName == null) { logger.error("request query parameter fileName must not be null"); throw new Exception("request query parameter fileName must not be null"); } if (mr.isCurrentUserAllowed(FilePermissionEnum.READ, fullFileName)) { CfrFile file = service.getRepository().getFile(fullFileName); setResponseHeaders(getMimeType(file.getFileName()), -1, URLEncoder.encode(file.getFileName(), CharsetHelper.getEncoding())); ByteArrayInputStream bais = new ByteArrayInputStream(file.getContent()); IOUtils.copy(bais, out); IOUtils.closeQuietly(bais); } else { getResponse().sendError(HttpServletResponse.SC_UNAUTHORIZED, "you don't have permissions to access the file"); } } @Exposed(accessLevel = AccessLevel.PUBLIC) public void viewFile(OutputStream out) throws IOException, JSONException, Exception { String fullFileName = checkRelativePathSanity(getRequestParameters().getStringParameter("fileName", null)); if (fullFileName == null) { logger.error("request query parameter fileName must not be null"); throw new Exception("request query parameter fileName must not be null"); } if (mr.isCurrentUserAllowed(FilePermissionEnum.READ, fullFileName)) { CfrFile file = service.getRepository().getFile(fullFileName); setResponseHeaders(getMimeType(file.getFileName()), -1, null); ByteArrayInputStream bais = new ByteArrayInputStream(file.getContent()); IOUtils.copy(bais, out); IOUtils.closeQuietly(bais); } else { getResponse().sendError(HttpServletResponse.SC_UNAUTHORIZED, "you don't have permissions to access the file"); } } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void listFilesJSON(OutputStream out) throws IOException, JSONException { String baseDir = checkRelativePathSanity(getRequestParameters().getStringParameter("dir", "")); IFile[] files = service.getRepository().listFiles(baseDir); JSONArray arr = new JSONArray(); if (files != null) { for (IFile file : files) { if (mr.isCurrentUserAllowed(FilePermissionEnum.READ, relativeFilePath(baseDir, file.getName()))) { JSONObject obj = new JSONObject(); obj.put("fileName", file.getName()); obj.put("isDirectory", file.isDirectory()); obj.put("path", baseDir); arr.put(obj); } } } writeOut(out, arr.toString(2)); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void listUploads(OutputStream out) throws IOException, JSONException { String path = checkRelativePathSanity(getRequestParameters().getStringParameter("fileName", "")); writeOut(out, mr.listFiles(path, getRequestParameters().getStringParameter("user", ""), getRequestParameters().getStringParameter("startDate", ""), getRequestParameters().getStringParameter("endDate", ""))); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void listUploadsFlat(OutputStream out) throws IOException, JSONException { String path = checkRelativePathSanity(getRequestParameters().getStringParameter("fileName", "")); writeOut(out, mr.listFilesFlat(path, getRequestParameters().getStringParameter("user", ""), getRequestParameters().getStringParameter("startDate", ""), getRequestParameters().getStringParameter("endDate", "")).toString(2)); } private static String toJQueryFileTree(String baseDir, IFile[] files, String[] extensions) { StringBuilder out = new StringBuilder(); out.append("<ul class=\"jqueryFileTree\" style=\"display: none;\">"); for (IFile file : files) { if (file.isDirectory()) { out.append("<li class=\"directory collapsed\"><a href=\"#\" rel=\"" + baseDir + file.getName() + "/\">" + file.getName() + "</a></li>"); } } for (IFile file : files) { if (!file.isDirectory()) { int dotIndex = file.getName().lastIndexOf('.'); String ext = dotIndex > 0 ? file.getName().substring(dotIndex + 1) : ""; boolean accepted = ext.equals(""); if (!ext.equals("")) { if (extensions == null || extensions.length == 0) { accepted = true; } else { for (String acceptedExtension : extensions) { if (ext.equals(acceptedExtension)) { accepted = true; break; } } } } if (accepted) { out.append("<li class=\"file ext_" + ext + "\"><a href=\"#\" rel=\"" + baseDir + file.getName() + "\">" + file.getName() + "</a></li>"); } } } out.append("</ul>"); return out.toString(); } private void redirectToCdeEditor(OutputStream out, Map<String, Object> params) throws IOException { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append("../pentaho-cdf-dd/edit"); if (params.size() > 0) { urlBuilder.append("?"); } List<String> paramArray = new ArrayList<String>(); for (String key : params.keySet()) { Object value = params.get(key); if (value instanceof String) { paramArray.add(key + "=" + URLEncoder.encode((String) value, getEncoding())); } } urlBuilder.append(StringUtils.join(paramArray, "&")); redirect(urlBuilder.toString()); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void setPermissions(OutputStream out) throws JSONException, IOException { String path = checkRelativePathSanity(getRequestParameters().getStringParameter(pathParameterPath, null)); String[] userOrGroupId = getRequestParameters().getStringArrayParameter(pathParameterGroupOrUserId, new String[] {}); String[] _permissions = getRequestParameters().getStringArrayParameter(pathParameterPermission, new String[] { FilePermissionEnum.READ.getId() }); JSONObject result = new JSONObject(); if (path != null && userOrGroupId.length > 0 && _permissions.length > 0) { // build valid permissions set Set<FilePermissionEnum> validPermissions = new TreeSet<FilePermissionEnum>(); for (String permission : _permissions) { FilePermissionEnum perm = FilePermissionEnum.resolve(permission); if (perm != null) { validPermissions.add(perm); } } JSONArray permissionAddResultArray = new JSONArray(); for (String id : userOrGroupId) { JSONObject individualResult = new JSONObject(); boolean storeResult = FileStorer .storeFilePermissions(new FilePermissionMetadata(path, id, validPermissions)); if (storeResult) { individualResult.put("status", String.format("Added permission for path %s and user/role %s", path, id)); } else { individualResult.put("status", String.format("Failed to add permission for path %s and user/role %s", path, id)); } permissionAddResultArray.put(individualResult); } result.put("status", "Operation finished. Check statusArray for details."); result.put("statusArray", permissionAddResultArray); } else { result.put("status", "Path or user group parameters not found"); } writeOut(out, result.toString(2)); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void deletePermissions(OutputStream out) throws JSONException, IOException { String path = checkRelativePathSanity(getRequestParameters().getStringParameter(pathParameterPath, null)); String[] userOrGroupId = getRequestParameters().getStringArrayParameter(pathParameterGroupOrUserId, new String[] {}); JSONObject result = new JSONObject(); if (path != null || (userOrGroupId != null && userOrGroupId.length > 0)) { if (userOrGroupId == null || userOrGroupId.length == 0) { if (FileStorer.deletePermissions(path, null)) { result.put("status", "Permissions deleted"); } else { result.put("status", "Error deleting permissions"); } } else { JSONArray permissionDeleteResultArray = new JSONArray(); for (String id : userOrGroupId) { JSONObject individualResult = new JSONObject(); boolean deleteResult = FileStorer.deletePermissions(path, id); if (deleteResult) { individualResult.put("status", String.format("Permission for %s and path %s deleted.", id, path)); } else { individualResult.put("status", String.format("Failed to delete permission for %s and path %s.", id, path)); } permissionDeleteResultArray.put(individualResult); } result.put("status", "Multiple permission deletion. Check Status array"); result.put("statusArray", permissionDeleteResultArray); } } else { result.put("status", "Required arguments user/role and path not found"); } writeOut(out, result.toString(2)); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeType.JSON) public void getPermissions(OutputStream out) throws IOException, JSONException { String path = checkRelativePathSanity(getRequestParameters().getStringParameter(pathParameterPath, null)); String id = getRequestParameters().getStringParameter(pathParameterGroupOrUserId, null); if (path != null || id != null) { JSONArray permissions = mr.getPermissions(path, id, FilePermissionMetadata.DEFAULT_PERMISSIONS); writeOut(out, permissions.toString(0)); } } @Exposed(accessLevel = AccessLevel.ADMIN) public void resetRepository(OutputStream out) { PersistenceEngine.getInstance().dropClass(FileStorer.FILE_METADATA_STORE_CLASS); PersistenceEngine.getInstance().initializeClass(FileStorer.FILE_METADATA_STORE_CLASS); PersistenceEngine.getInstance().dropClass(FileStorer.FILE_PERMISSIONS_METADATA_STORE_CLASS); PersistenceEngine.getInstance().initializeClass(FileStorer.FILE_PERMISSIONS_METADATA_STORE_CLASS); IFileRepository repo = new CfrService().getRepository(); for (IFile file : repo.listFiles("")) { repo.deleteFile(file.getFullPath()); } } private static final String pathParameterGroupOrUserId = "id"; private static final String pathParameterPath = "path"; private static final String pathParameterPermission = "permission"; @Exposed(accessLevel = AccessLevel.PUBLIC) public void checkVersion(OutputStream out) throws IOException, JSONException { writeOut(out, getVersionChecker().checkVersion()); } @Exposed(accessLevel = AccessLevel.PUBLIC) public void getVersion(OutputStream out) throws IOException, JSONException { writeOut(out, getVersionChecker().getVersion()); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeTypes.JSON) public void about(OutputStream out) throws IOException, JSONException { renderInCde(out, getRenderRequestParameters("cfrAbout.wcdf")); } @Exposed(accessLevel = AccessLevel.PUBLIC, outputType = MimeTypes.JSON) public void browser(OutputStream out) throws IOException, JSONException { renderInCde(out, getRenderRequestParameters("cfrBrowser.wcdf")); } public VersionChecker getVersionChecker() { return new VersionChecker(new CfrPluginSettings()) { @Override protected String getVersionCheckUrl(VersionChecker.Branch branch) { switch (branch) { case TRUNK: return "http://ci.pentaho.com/job/pentaho-cfr-pentaho/lastSuccessfulBuild/artifact/cfr-pentaho/dist/marketplace.xml"; // case STABLE: // return "http://ci.analytical-labs.com/job/Webdetails-CFR-Release/" // + "lastSuccessfulBuild/artifact/dist/marketplace.xml"; default: return null; } } }; } private void renderInCde(OutputStream out, Map<String, Object> params) throws IOException { InterPluginCall pluginCall = new InterPluginCall(InterPluginCall.CDE, "Render", params); pluginCall.setResponse(getResponse()); pluginCall.setRequest(getRequest()); pluginCall.setOutputStream(out); pluginCall.run(); } private Map<String, Object> getRenderRequestParameters(String dashboardName) { Map<String, Object> params = new HashMap<String, Object>(); params.put("solution", "system"); params.put("path", UI_PATH); params.put("file", dashboardName); params.put("bypassCache", "true"); params.put("absolute", "false"); params.put("inferScheme", "false"); params.put("root", getRoot()); // add request parameters ServletRequest request = getRequest(); @SuppressWarnings("unchecked") // should always be String Enumeration<String> originalParams = request.getParameterNames(); // Iterate and put the values there while (originalParams.hasMoreElements()) { String originalParam = originalParams.nextElement(); params.put(originalParam, request.getParameter(originalParam)); } return params; } private String getRoot() { ServletRequest wrapper = getRequest(); String root = wrapper.getScheme() + "://" + wrapper.getServerName() + ":" + wrapper.getServerPort(); return root; } }