/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* 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 org.jkiss.dbeaver.tools.project;
import org.jkiss.dbeaver.Log;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IExportWizard;
import org.eclipse.ui.IWorkbench;
import org.jkiss.dbeaver.core.CoreMessages;
import org.jkiss.dbeaver.core.DBeaverCore;
import org.jkiss.dbeaver.core.DBeaverUI;
import org.jkiss.dbeaver.model.connection.DBPDriverLibrary;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress;
import org.jkiss.dbeaver.registry.*;
import org.jkiss.dbeaver.registry.driver.DriverDescriptor;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.xml.XMLBuilder;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ProjectExportWizard extends Wizard implements IExportWizard {
private static final Log log = Log.getLog(ProjectExportWizard.class);
public static final int COPY_BUFFER_SIZE = 5000;
public static final String PROJECT_DESC_FILE = ".project";
private ProjectExportWizardPage mainPage;
public ProjectExportWizard() {
}
@Override
public void init(IWorkbench workbench, IStructuredSelection selection) {
setWindowTitle(CoreMessages.dialog_project_export_wizard_window_title);
setNeedsProgressMonitor(true);
mainPage = new ProjectExportWizardPage(CoreMessages.dialog_project_export_wizard_main_page);
}
@Override
public void addPages() {
super.addPages();
addPage(mainPage);
}
@Override
public boolean performFinish() {
final ProjectExportData exportData = mainPage.getExportData();
try {
DBeaverUI.run(getContainer(), true, true, new DBRRunnableWithProgress() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
exportProjects(monitor, exportData);
} catch (Exception e) {
throw new InvocationTargetException(e);
}
}
});
}
catch (InterruptedException ex) {
return false;
}
catch (InvocationTargetException ex) {
UIUtils.showErrorDialog(
getShell(),
"Export error",
"Cannot export projects",
ex.getTargetException());
return false;
}
return true;
}
public void exportProjects(DBRProgressMonitor monitor, final ProjectExportData exportData)
throws IOException, CoreException, InterruptedException
{
if (!exportData.getOutputFolder().exists()) {
if (!exportData.getOutputFolder().mkdirs()) {
throw new IOException("Cannot create directory '" + exportData.getOutputFolder().getAbsolutePath() + "'"); //$NON-NLS-2$
}
}
String archiveName = exportData.getArchiveFileName() + ExportConstants.ARCHIVE_FILE_EXT;
File archiveFile = new File(exportData.getOutputFolder(), archiveName);
FileOutputStream exportStream = new FileOutputStream(archiveFile);
try {
ByteArrayOutputStream metaBuffer = new ByteArrayOutputStream(10000);
ZipOutputStream archiveStream = new ZipOutputStream(exportStream);
// Start meta
XMLBuilder meta = new XMLBuilder(metaBuffer, GeneralUtils.UTF8_ENCODING);
meta.startElement(ExportConstants.TAG_ARCHIVE);
meta.addAttribute(ExportConstants.ATTR_VERSION, ExportConstants.ARCHIVE_VERSION_CURRENT);
exportData.initExport(DBeaverCore.getInstance().getProjectRegistry(), meta, archiveStream);
{
// Export source info
meta.startElement(ExportConstants.TAG_SOURCE);
meta.addAttribute(ExportConstants.ATTR_TIME, Long.valueOf(System.currentTimeMillis()));
meta.addAttribute(ExportConstants.ATTR_ADDRESS, InetAddress.getLocalHost().getHostAddress());
meta.addAttribute(ExportConstants.ATTR_HOST, InetAddress.getLocalHost().getHostName());
meta.endElement();
}
Map<IProject, Integer> resCountMap = new HashMap<>();
monitor.beginTask(CoreMessages.dialog_project_export_wizard_monitor_collect_info, exportData.getProjectsToExport().size());
for (IProject project : exportData.getProjectsToExport()) {
// Add used drivers to export data
final DataSourceRegistry dataSourceRegistry = exportData.projectRegistry.getDataSourceRegistry(project);
if (dataSourceRegistry != null) {
for (DataSourceDescriptor dataSourceDescriptor : dataSourceRegistry.getDataSources()) {
exportData.usedDrivers.add(dataSourceDescriptor.getDriver());
}
}
resCountMap.put(project, getChildCount(exportData, project));
monitor.worked(1);
}
monitor.done();
{
// Export drivers meta
monitor.beginTask(CoreMessages.dialog_project_export_wizard_monitor_export_driver_info, 1);
exportData.meta.startElement(RegistryConstants.TAG_DRIVERS);
for (DriverDescriptor driver : exportData.usedDrivers) {
driver.serialize(exportData.meta, true);
}
exportData.meta.endElement();
monitor.done();
}
{
// Export projects
exportData.meta.startElement(ExportConstants.TAG_PROJECTS);
for (IProject project : exportData.getProjectsToExport()) {
monitor.beginTask(NLS.bind(CoreMessages.dialog_project_export_wizard_monitor_export_project, project.getName()), resCountMap.get(project));
try {
exportProject(monitor, exportData, project);
} finally {
monitor.done();
}
}
exportData.meta.endElement();
}
if (exportData.isExportDrivers()) {
// Export driver libraries
Set<File> libFiles = new HashSet<>();
Map<String, File> libPathMap = new HashMap<>();
for (DriverDescriptor driver : exportData.usedDrivers) {
for (DBPDriverLibrary fileDescriptor : driver.getDriverLibraries()) {
final File libraryFile = fileDescriptor.getLocalFile();
if (libraryFile != null && !fileDescriptor.isDisabled() && libraryFile.exists()) {
libFiles.add(libraryFile);
libPathMap.put(fileDescriptor.getPath(), libraryFile);
}
}
}
if (!libFiles.isEmpty()) {
monitor.beginTask(CoreMessages.dialog_project_export_wizard_monitor_export_libraries, libFiles.size());
final ZipEntry driversFolder = new ZipEntry(ExportConstants.DIR_DRIVERS + "/"); //$NON-NLS-1$
driversFolder.setComment("Database driver libraries"); //$NON-NLS-1$
exportData.archiveStream.putNextEntry(driversFolder);
exportData.archiveStream.closeEntry();
exportData.meta.startElement(ExportConstants.TAG_LIBRARIES);
Set<String> libFileNames = new HashSet<>();
for (String libPath : libPathMap.keySet()) {
final File libFile = libPathMap.get(libPath);
// Check for file name duplications
final String libFileName = libFile.getName();
if (libFileNames.contains(libFileName)) {
log.warn("Duplicate driver library file name: " + libFileName); //$NON-NLS-1$
continue;
}
libFileNames.add(libFileName);
monitor.subTask(libFileName);
exportData.meta.startElement(RegistryConstants.TAG_FILE);
exportData.meta.addAttribute(ExportConstants.ATTR_PATH, libPath);
exportData.meta.addAttribute(ExportConstants.ATTR_FILE, "drivers/" + libFileName); //$NON-NLS-1$
exportData.meta.endElement();
final ZipEntry driverFile = new ZipEntry(ExportConstants.DIR_DRIVERS + "/" + libFileName); //$NON-NLS-1$
driverFile.setComment("Driver library"); //$NON-NLS-1$
exportData.archiveStream.putNextEntry(driverFile);
try (InputStream is = new FileInputStream(libFile)) {
IOUtils.copyStream(is, exportData.archiveStream, COPY_BUFFER_SIZE);
}
exportData.archiveStream.closeEntry();
monitor.worked(1);
}
exportData.meta.endElement();
monitor.done();
}
}
// Add meta to archive
{
exportData.meta.endElement();
exportData.meta.flush();
archiveStream.putNextEntry(new ZipEntry(ExportConstants.META_FILENAME));
archiveStream.write(metaBuffer.toByteArray());
archiveStream.closeEntry();
}
// Finish archive creation
archiveStream.finish();
} finally {
ContentUtils.close(exportStream);
}
}
private int getChildCount(ProjectExportData exportData, IResource resource) throws CoreException
{
if (exportData.projectRegistry.getResourceHandler(resource) == null) {
return 0;
}
int childCount = 1;
if (resource instanceof IContainer) {
for (IResource child : ((IContainer) resource).members()) {
childCount += getChildCount(exportData, child);
}
}
return childCount;
}
private void exportProject(DBRProgressMonitor monitor, ProjectExportData exportData, IProject project) throws InterruptedException, CoreException, IOException
{
monitor.subTask(project.getName());
// Refresh project
project.refreshLocal(IResource.DEPTH_INFINITE, RuntimeUtils.getNestedMonitor(monitor));
// Write meta info
exportData.meta.startElement(ExportConstants.TAG_PROJECT);
exportData.meta.addAttribute(ExportConstants.ATTR_NAME, project.getName());
exportData.meta.addAttribute(ExportConstants.ATTR_DESCRIPTION, project.getDescription().getComment());
saveResourceProperties(project, exportData.meta);
// Add project folder
final String projectPath = ExportConstants.DIR_PROJECTS + "/" + project.getName() + "/"; //$NON-NLS-1$ //$NON-NLS-2$
exportData.archiveStream.putNextEntry(new ZipEntry(projectPath));
exportData.archiveStream.closeEntry();
// Export resources
for (IResource child : project.members(IContainer.INCLUDE_HIDDEN)) {
exportResourceTree(monitor, exportData, projectPath, child);
}
exportData.meta.endElement();
monitor.worked(1);
}
private void exportResourceTree(DBRProgressMonitor monitor, ProjectExportData exportData, String parentPath, IResource resource) throws CoreException, IOException
{
if (resource.getName().equals(PROJECT_DESC_FILE)) {
// Skip it
return;
}
monitor.subTask(parentPath + resource.getName());
exportData.meta.startElement(ExportConstants.TAG_RESOURCE);
exportData.meta.addAttribute(ExportConstants.ATTR_NAME, resource.getName());
saveResourceProperties(resource, exportData.meta);
if (resource instanceof IContainer) {
// Add folder entry
parentPath = parentPath + resource.getName() + "/"; //$NON-NLS-1$
exportData.archiveStream.putNextEntry(new ZipEntry(parentPath));
exportData.archiveStream.closeEntry();
// Export children
final IResource[] members = ((IContainer) resource).members();
for (IResource child : members) {
if (child.isLinked()) {
continue;
}
exportResourceTree(monitor, exportData, parentPath, child);
}
} else if (resource instanceof IFile) {
// Add file to archive
IFile file = (IFile)resource;
exportData.archiveStream.putNextEntry(new ZipEntry(parentPath + resource.getName()));
try (InputStream is = file.getContents()) {
IOUtils.copyStream(is, exportData.archiveStream, COPY_BUFFER_SIZE);
}
exportData.archiveStream.closeEntry();
} else {
// Just skip it
}
exportData.meta.endElement();
monitor.worked(1);
}
private void saveResourceProperties(IResource resource, XMLBuilder xml) throws CoreException, IOException
{
if (resource instanceof IFile) {
final IContentDescription contentDescription = ((IFile) resource).getContentDescription();
if (contentDescription != null && contentDescription.getCharset() != null) {
xml.addAttribute(ExportConstants.ATTR_CHARSET, contentDescription.getCharset());
//xml.addAttribute(ExportConstants.ATTR_CHARSET, contentDescription.getContentType());
}
} else if (resource instanceof IFolder) {
xml.addAttribute(ExportConstants.ATTR_DIRECTORY, true);
}
for (Object entry : resource.getPersistentProperties().entrySet()) {
Map.Entry<?, ?> propEntry = (Map.Entry<?,?>) entry;
xml.startElement(ExportConstants.TAG_ATTRIBUTE);
final QualifiedName attrName = (QualifiedName) propEntry.getKey();
xml.addAttribute(ExportConstants.ATTR_QUALIFIER, attrName.getQualifier());
xml.addAttribute(ExportConstants.ATTR_NAME, attrName.getLocalName());
xml.addAttribute(ExportConstants.ATTR_VALUE, (String) propEntry.getValue());
xml.endElement();
}
}
}