diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java index 0a24f2e1019f4b6ee05fc791cad622d80b326061..71f75ce8996df4f3c4a659442becc390284c71aa 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java @@ -55,6 +55,24 @@ public interface IProjectDAO { @PreAuthorize("hasAnyRole('User', 'Administrator')") public abstract void addProject(String projectName, final String username) throws ProjectAlreadyExistingException, IOException; + /** + * This method imports an existing kax-file into the application. If the given project name does already exist, the application will not try to upload it in the + * first place. + * + * @param projectName + * The name of the new project. + * @param username + * The name of the user who imported the project. + * @param file + * The kax file to be uploaded. + * @throws ProjectAlreadyExistingException + * If a project with the same name exists already. + * @throws IOException + * If something went wrong during the creation of the project or the uploading of the file. + */ + @PreAuthorize("hasAnyRole('User', 'Administrator')") + public void importProject(final String projectName, final String username, final UploadedFile file) throws ProjectAlreadyExistingException, IOException; + /** * This method makes a copy of a project and saves it under another name. If the method fails due to an {@link IOException}, it will make sure that the * project-directories of the destination-project will be removed as if the method would never have been called. diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java index 18dc09930b2d27482b9382843018b27c55d9c004..0ee21f84323dd1a93dde5ff149f86ce1099dbe8b 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java @@ -161,7 +161,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { try { final Object dummyObject = new Object(); // Deliver a dummy object as a requester to make sure that the classloader can be disposed. Once the program exits this scope, it can be released. - final CloseableURLClassLoader classLoader = (CloseableURLClassLoader) this.getClassLoader(project, dummyObject); + final CloseableURLClassLoader classLoader = (CloseableURLClassLoader) this.getClassLoader(project, dummyObject); // NOPMD (No ordinary classloader) final ClassAndMethodContainer classAndMethodContainer = new ClassAndMethodContainer(classLoader); final List<PluginContainer> readers = new ArrayList<PluginContainer>(); @@ -842,6 +842,31 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.persistence.IProjectDAO#importProject(java.lang.String, java.lang.String, org.primefaces.model.UploadedFile) + */ + @Override + @PreAuthorize("hasAnyRole('User', 'Administrator')") + public void importProject(final String projectName, final String username, final UploadedFile file) throws ProjectAlreadyExistingException, IOException { + // Add an empty project. + this.addProject(projectName, username); + + // Now upload the kax file + final File tempFile = File.createTempFile("java", ".tmp"); + + final BufferedInputStream in = new BufferedInputStream(file.getInputstream()); + final BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile)); + + // Copy the data - the streams will be closed in this method. + FileCopyUtils.copy(in, out); + + // Now load the kax file and use the resulting MIProject to overwrite the existing (newly created) kax file + final MIProject project = AnalysisController.loadFromFile(tempFile); + this.saveProject(projectName, project, 0, true); + } + /* * (non-Javadoc) * diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java index dc107b215d4260bdd8ccfe47b200d931f3330777..fed3a397e2664193576dde521b31b4b859831b47 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java @@ -60,6 +60,24 @@ public interface IProjectService { @PreAuthorize("hasAnyRole('User', 'Administrator')") public void addProject(final String projectName, final String username) throws ProjectAlreadyExistingException, IOException; + /** + * This method imports an existing kax-file into the application. If the given project name does already exist, the application will not try to upload it in the + * first place. + * + * @param projectName + * The name of the new project. + * @param username + * The name of the user who imported the project. + * @param file + * The kax file to be uploaded. + * @throws ProjectAlreadyExistingException + * If a project with the same name exists already. + * @throws IOException + * If something went wrong during the creation of the project or the uploading of the file. + */ + @PreAuthorize("hasAnyRole('User', 'Administrator')") + public void importProject(final String projectName, final String username, final UploadedFile file) throws ProjectAlreadyExistingException, IOException; + /** * This method makes a copy of a project and saves it under another name. If the method fails due to an {@link IOException}, it will make sure that the * project-directories of the destination-project will be removed as if the method would never have been called. diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java index 02c9e8f36bf23f5058c20f2cf5f7d1eb80bde645..4873299fd8ea50223437204524bd7528dc8989d0 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java @@ -62,6 +62,11 @@ public final class ProjectServiceImpl implements IProjectService { // No code necessary. } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#addProject(java.lang.String, java.lang.String) + */ @Override public void addProject(final String projectName, final String username) throws ProjectAlreadyExistingException, IOException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -71,6 +76,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#copyProject(java.lang.String, java.lang.String) + */ @Override public void copyProject(final String originalProjectName, final String newProjectName) throws ProjectNotExistingException, ProjectAlreadyExistingException, IOException { @@ -98,6 +108,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#openProject(java.lang.String) + */ @Override public MIProject openProject(final String projectName) throws ProjectNotExistingException, IOException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -107,6 +122,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#openProject(java.lang.String, kieker.webgui.common.ClassAndMethodContainer) + */ @Override public Object openProject(final String projectName, final ClassAndMethodContainer classAndMethodContainer) throws ProjectNotExistingException, IOException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -116,6 +136,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#saveProject(java.lang.String, kieker.analysis.model.analysisMetaModel.MIProject, long, boolean) + */ @Override public void saveProject(final String projectName, final MIProject project, final long timeStamp, final boolean overwriteNewerProject) throws ProjectNotExistingException, IOException, NewerProjectException { @@ -126,6 +151,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getCurrTimeStamp(java.lang.String) + */ @Override public long getCurrTimeStamp(final String projectName) throws ProjectNotExistingException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -135,6 +165,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#uploadLibrary(org.primefaces.model.UploadedFile, java.lang.String) + */ @Override public void uploadLibrary(final UploadedFile file, final String projectName) throws ProjectNotExistingException, IOException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -144,6 +179,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getClassLoader(java.lang.String, java.lang.Object) + */ @Override public ClassLoader getClassLoader(final String projectName, final Object requester) throws ProjectNotExistingException, IOException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -153,6 +193,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getAvailableComponents(java.lang.String) + */ @Override public ComponentListContainer getAvailableComponents(final String projectName) { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -162,6 +207,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#listAllLibraries(java.lang.String) + */ @Override public List<String> listAllLibraries(final String projectName) throws ProjectNotExistingException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -171,11 +221,21 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#listAllProjects() + */ @Override public Collection<String> listAllProjects() { return this.projectDAO.listAllProjects(); } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#initializeAnalysis(java.lang.String, java.lang.ClassLoader) + */ @Override public void initializeAnalysis(final String projectName, final ClassLoader classLoader) throws ProjectNotExistingException, AnalysisStateException, AnalysisInitializationException { @@ -190,6 +250,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#cleanAnalysis(java.lang.String) + */ @Override public void cleanAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { final Object analysisLock = this.getLock(projectName, this.analysesLocks); @@ -199,6 +264,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#startAnalysis(java.lang.String) + */ @Override public void startAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { final Object analysisLock = this.getLock(projectName, this.analysesLocks); @@ -208,6 +278,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#stopAnalysis(java.lang.String) + */ @Override public void stopAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { final Object analysisLock = this.getLock(projectName, this.analysesLocks); @@ -217,6 +292,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getDisplay(java.lang.String, java.lang.String, java.lang.String) + */ @Override public Object getDisplay(final String projectName, final String viewName, final String displayName) throws ProjectNotExistingException, DisplayNotFoundException { @@ -227,6 +307,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getCurrentState(java.lang.String) + */ @Override public STATE getCurrentState(final String projectName) { final Object analysisLock = this.getLock(projectName, this.analysesLocks); @@ -261,6 +346,11 @@ public final class ProjectServiceImpl implements IProjectService { return lock; } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getLogEntries(java.lang.String) + */ @Override public Object[] getLogEntries(final String projectName) throws AnalysisStateException { final Object analysisLock = this.getLock(projectName, this.analysesLocks); @@ -284,6 +374,11 @@ public final class ProjectServiceImpl implements IProjectService { } } + /* + * (non-Javadoc) + * + * @see kieker.webgui.service.IProjectService#getOwner(java.lang.String) + */ @Override public String getOwner(final String projectName) throws ProjectNotExistingException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); @@ -292,4 +387,13 @@ public final class ProjectServiceImpl implements IProjectService { return this.projectDAO.getOwner(projectName); } } + + @Override + public void importProject(final String projectName, final String username, final UploadedFile file) throws ProjectAlreadyExistingException, IOException { + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + + synchronized (projectLock) { + this.projectDAO.importProject(projectName, username, file); + } + } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java index 2b2f42e7ad40ebd37b78de6fe3e585e6bb81f413..804d2cbfe962d5a9d692b9243804000f2ec61f06 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java @@ -40,6 +40,8 @@ import kieker.webgui.service.IProjectService; import kieker.webgui.web.beans.session.UserBean; import kieker.webgui.web.beans.view.CurrentProjectOverviewBean; +import org.primefaces.model.UploadedFile; + /** * The {@link ProjectsBean} is a Spring managed bean to manage a list with all application wide available projects. It provides methods to receive this list as well * as methods to add, create, rename, open and copy projects. Furthermore the state of existing projects (like the timestamp or the state of the analysis) can be @@ -148,6 +150,25 @@ public final class ProjectsBean { } } + public void uploadProject(final String projectName, final UploadedFile file, final CurrentProjectOverviewBean currentProjectOverviewBean, final UserBean userBean) { + try { + // Try and use the FS-Manager to create the project atomically. + this.projectService.importProject(projectName, userBean.getUsername(), file); + // If there were no exception, everything went well. We can add the project to our list. + this.projects.add(projectName); + // Inform the user + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, "Project imported."); + // Update the list of the "calling" bean + currentProjectOverviewBean.updateLists(); + } catch (final IOException ex) { + ProjectsBean.LOG.error("An error occured while importing the project.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while importing the project."); + } catch (final ProjectAlreadyExistingException ex) { + ProjectsBean.LOG.info("A project with the same name exists already.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_WARN, "A project with the same name exists already."); + } + } + /** * This method can be used to open an already existing project. This means that the current state of the project on the file system is loaded into an instance of * {@link MIProject}. This instance can be modified at will and for example later saved by the {@link IProjectService}. diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/request/UploadFileBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/request/UploadFileBean.java new file mode 100644 index 0000000000000000000000000000000000000000..ae74d638eafea2c359665f8765e92def5ab20c5e --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/request/UploadFileBean.java @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright 2012 Kieker Project (http://kieker-monitoring.net) + * + * 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 kieker.webgui.web.beans.request; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import org.primefaces.model.UploadedFile; + +/** + * This simple bean is request scoped and can be used to store the necessary data for a new user during a request. It is not to be used to deliver a list of users + * for example, as there is a field for a password. + * + * @author Nils Christian Ehmke + */ +@Component +@Scope("request") +public class UploadFileBean { + + private UploadedFile file = null; + + /** + * Creates a new instance of this bean and initializes it with empty fields. <b>Do not use this constructor. This bean is Spring managed.</b> + */ + public UploadFileBean() { + // No code necessary + } + + /** + * Getter for the property {@link UploadFileBean#file}. + * + * @return The current value of the property. + */ + public UploadedFile getFile() { + return this.file; + } + + /** + * Setter for the property {@link UploadFileBean#file}. + * + * @param file + * The new value of the property. + */ + public void setFile(final UploadedFile file) { + this.file = file; + } + +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java index 1301504c830b143428045c144a3e18219a973e76..4375516a39aab5b27c89615c8eaa3084522ad8df 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java @@ -591,7 +591,7 @@ public final class CurrentAnalysisEditorGraphBean { * @return The escaped string. */ private static final String simpleEscape(final String str) { - return str.replaceAll("\'", ""); + return str != null ? str.replaceAll("\'", "") : str; } /** diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml index efe3274872750188ede4fdff1e463fe708c16e8d..81a75b76bf93017e0554d48696231e8d90e106ae 100644 --- a/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml +++ b/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml @@ -85,4 +85,23 @@ </div> </h:form> </p:dialog> + + <p:dialog id="importProjectDlg" header="#{localizedProjectOverviewMessages.importProject}" resizable="false" modal="true" widgetVar="importProjectDialog"> + <!-- Make sure that closing of the dialog also clears the input field. --> + <p:ajax event="close" update="importProjectDialogForm:newProjectInputText" /> + + <h:form id="importProjectDialogForm" enctype="multipart/form-data"> + <div style="text-align: left"> + <h:outputText value="#{localizedProjectOverviewMessages.name}: " /> + <p:inputText id="newProjectInputText" value="#{stringBean.string}" style="width: 95%" /> + <br/> <br/> + <p:fileUpload allowTypes="kax" mode="simple" value="#{uploadFileBean.file}"/> + </div> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="#{localizedMessages.ok}" update=":projectsListForm :messages" action="#{projectsBean.uploadProject(stringBean.string, uploadFileBean.file, currentProjectOverviewBean, userBean)}" ajax="false" oncomplete="importProjectDialog.hide()" /> + </div> + </h:form> + </p:dialog> </ui:composition> \ No newline at end of file diff --git a/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml index 652595611e778cf2699d0cbeaee5ddfec14c0792..95d28cffcc940b1567a211de7b5fe98d7cf38f25 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml @@ -30,7 +30,7 @@ <p:submenu label="#{localizedMessages.file}"> <c:if test="#{sec:areAnyGranted('User, Administrator')}"> <p:menuitem styleClass="element-with-whitespace" icon="ui-icon-newProject" value=" #{localizedProjectOverviewMessages.newProject}" onclick="newProjectDialog.show()" ajax="true"/> - <p:menuitem styleClass="element-with-whitespace" icon="ui-icon-importProject" value=" #{localizedProjectOverviewMessages.importProject}" ajax="true" disabled="true"/> + <p:menuitem styleClass="element-with-whitespace" icon="ui-icon-importProject" value=" #{localizedProjectOverviewMessages.importProject}" onclick="importProjectDialog.show()" ajax="true"/> <p:separator/> </c:if> <p:menuitem styleClass="element-with-whitespace" icon="ui-icon-reload" value=" #{localizedProjectOverviewMessages.refreshProjectsList}" update=":projectsListForm" action="#{currentProjectOverviewBean.updateLists()}" ajax="true"/>