Skip to content
Snippets Groups Projects
Commit 1df5ffdf authored by Nils Christian Ehmke's avatar Nils Christian Ehmke
Browse files

Projects can now be copied

parent 61630b19
No related branches found
No related tags found
No related merge requests found
...@@ -82,9 +82,9 @@ public final class ProjectsBean { ...@@ -82,9 +82,9 @@ public final class ProjectsBean {
} }
/** /**
* This method adds a new project with the given name to the application if this is possible. In either case (this means if the project has been created, if he * This method adds a new project with the given name to the application if this is possible. In either case (this means if the project has been created, if the
* project could not be created for any reason) the user will be informed via the growl component. In other words: A message will be delivered within the current * project could not be created for any reason) the user will be informed via the growl component. In other words: A message will be delivered within the current
* context.The creation of a new project means in this context that a directory with its name will be created and that an empty but valid kax-file will be * context. The creation of a new project means in this context that a directory with its name will be created and that an empty but valid kax-file will be
* produced. After creating the project (provided that he creation was successful) the projects list of the "calling" {@link CurrentProjectOverviewBean} will be * produced. After creating the project (provided that he creation was successful) the projects list of the "calling" {@link CurrentProjectOverviewBean} will be
* updated.<br> * updated.<br>
* If something should go wrong, no exception will be thrown. Everything is caught if necessary. * If something should go wrong, no exception will be thrown. Everything is caught if necessary.
...@@ -111,6 +111,38 @@ public final class ProjectsBean { ...@@ -111,6 +111,38 @@ public final class ProjectsBean {
} }
} }
/**
* This method tries to copy the project with the given name and saves it under the given new name. In either case (this means if the project has been created,
* if the project could not be created for any reason) the user will be informed via the growl component. In other words: A message will be delivered within the
* current context. After creating the project (provided that he creation was successful) the projects list of the "calling" {@link CurrentProjectOverviewBean}
* will be updated.<br>
* If something should go wrong, no exception will be thrown. Everything is caught if necessary.
*
* @param sourceProject
* The name of the source project.
* @param destinationProject
* The name of the new (copied) project.
*/
public void copyProject(final String sourceProject, final String destinationProject) {
try {
// Try and use the FS-Manager to copy the project atomically.
FSManager.getInstance().copyProject(sourceProject, destinationProject);
// If there were no exception, everything went well. We can add the project to our list.
this.projects.add(destinationProject);
// Inform the user
ProjectsBean.showMessage(FacesMessage.SEVERITY_INFO, "Project created.");
// Update the list of the "calling" bean
CurrentProjectOverviewBean.getCurrentInstance().updateLists();
} catch (final IOException ex) {
ProjectsBean.LOG.error("An error occured while creating the project.", ex);
ProjectsBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while creating the project.");
ex.printStackTrace();
} catch (final ProjectAlreadyExistingException ex) {
ProjectsBean.LOG.info("A project with the same name exists already.", ex);
ProjectsBean.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 * 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 FSManager}. * {@link MIProject}. This instance can be modified at will and for example later saved by the {@link FSManager}.
......
...@@ -23,12 +23,15 @@ package kieker.webgui.common; ...@@ -23,12 +23,15 @@ package kieker.webgui.common;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.nio.channels.ByteChannel;
import java.nio.channels.FileChannel;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.text.DecimalFormat; import java.text.DecimalFormat;
...@@ -82,9 +85,9 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F ...@@ -82,9 +85,9 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F
*/ */
private static final String ROOT_DIRECTORY = "data"; private static final String ROOT_DIRECTORY = "data";
/** /**
* This is the buffer (in bytes) used to copy files. * This is the buffer (in bytes) used to copy and upload files.
*/ */
private static final int BUF_SIZE_BYTES = 1024; private static final int BUF_SIZE_BYTES = 1024 * 1024;
/** /**
* This is the singleton instance of this class. * This is the singleton instance of this class.
*/ */
...@@ -337,11 +340,83 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F ...@@ -337,11 +340,83 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F
throw new ProjectAlreadyExistingException("A project with the name '" + newName + "' exists already."); throw new ProjectAlreadyExistingException("A project with the name '" + newName + "' exists already.");
} }
// TODO Copy all files // Get the necessary paths
final File dstProjDir = new File(FSManager.ROOT_DIRECTORY + File.separator + newName);
final File srcLibDir = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY);
final File dstLibDir = new File(FSManager.ROOT_DIRECTORY + File.separator + newName + File.separator + FSManager.LIB_DIRECTORY);
final File srcKaxFile = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + projectName + "."
+ FSManager.KAX_EXTENSION);
final File dstKaxFile = new File(FSManager.ROOT_DIRECTORY + File.separator + newName + File.separator + newName + "." + FSManager.KAX_EXTENSION);
// Create the directories
final boolean projDirCreated = dstProjDir.mkdir();
final boolean libDirCreated = dstLibDir.mkdir();
// The following part is only necessary to calm FindBugs...
@SuppressWarnings("unused")
final boolean createResults = projDirCreated && libDirCreated;
// Copy the kax file
this.copyFile(srcKaxFile, dstKaxFile);
// Copy the libs
for (final File lib : srcLibDir.listFiles()) {
final File dstLibFile = new File(dstLibDir, lib.getName());
this.copyFile(lib, dstLibFile);
}
} }
} }
} }
/**
* This method copies the given source file to the given destination file.
*
* @param src
* The source element. This should be a file.
* @param dst
* The destination element. This should be a (non existing) file.
* @throws IOException
* If something went wrong
*/
private void copyFile(final File src, final File dst) throws IOException {
// Open the files
final FileInputStream fileInputStream = new FileInputStream(src);
final FileOutputStream fileOutputStream = new FileOutputStream(dst);
final FileChannel inputChannel = fileInputStream.getChannel();
final FileChannel outputChannel = fileOutputStream.getChannel();
// Copy the data
this.transfer(inputChannel, outputChannel, src.length());
// Close the streams
fileInputStream.close();
fileOutputStream.close();
dst.setLastModified(src.lastModified());
}
/**
* This method transfers data from one channel to the other.
*
* @param fileChannel
* The input channel.
* @param byteChannel
* The output channel.
* @param lengthInBytes
* The size in bytes to be copied.
* @throws IOException
* If something went wrong.
*/
private void transfer(final FileChannel fileChannel, final ByteChannel byteChannel, final long lengthInBytes) throws IOException {
long overallBytesTransfered = 0L;
while (overallBytesTransfered < lengthInBytes) {
final long count = Math.min(FSManager.BUF_SIZE_BYTES, lengthInBytes - overallBytesTransfered);
final long bytesTransfered = fileChannel.transferTo(overallBytesTransfered, count, byteChannel);
overallBytesTransfered += bytesTransfered;
}
}
/** /**
* Checks whether a project with the name exists on the file system. * Checks whether a project with the name exists on the file system.
* *
......
...@@ -69,7 +69,7 @@ ...@@ -69,7 +69,7 @@
<p:menuitem icon="ui-icon-wrench" id="editAnalysisViews" styleClass="element-with-whitespace" value=" Cockpit Editor" ajax="false" action="#{currentCockpitEditorBean.setProject(project)}" /> <p:menuitem icon="ui-icon-wrench" id="editAnalysisViews" styleClass="element-with-whitespace" value=" Cockpit Editor" ajax="false" action="#{currentCockpitEditorBean.setProject(project)}" />
<p:menuitem icon="ui-icon-image" id="showAnalysis" styleClass="element-with-whitespace" value=" Cockpit" ajax="false" action="#{currentCockpitBean.setProject(project)}" /> <p:menuitem icon="ui-icon-image" id="showAnalysis" styleClass="element-with-whitespace" value=" Cockpit" ajax="false" action="#{currentCockpitBean.setProject(project)}" />
<p:separator/> <p:separator/>
<p:menuitem id="copyButton" icon="ui-icon-copy" styleClass="element-with-whitespace" value=" Copy Project" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="copyProjectDialog.show()" disabled="true"/> <p:menuitem id="copyButton" icon="ui-icon-copy" styleClass="element-with-whitespace" value=" Copy Project" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="copyProjectDialog.show()"/>
<p:menuitem id="renameButton" icon="ui-icon-pencil" styleClass="element-with-whitespace" value=" Rename Project" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="renameProjectDialog.show()" disabled="true"/> <p:menuitem id="renameButton" icon="ui-icon-pencil" styleClass="element-with-whitespace" value=" Rename Project" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="renameProjectDialog.show()" disabled="true"/>
<p:menuitem id="deleteButton" icon="ui-icon-trash" styleClass="element-with-whitespace" value=" Delete Project" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="deleteProjectDialog.show()" disabled="true"/> <p:menuitem id="deleteButton" icon="ui-icon-trash" styleClass="element-with-whitespace" value=" Delete Project" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="deleteProjectDialog.show()" disabled="true"/>
</p:menu> </p:menu>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment