/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.directory.studio.apacheds.configuration.editor;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.List;
import java.util.UUID;
import org.apache.directory.api.util.FileUtils;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.csn.CsnFactory;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.ldif.LdifEntry;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.server.config.ConfigWriter;
import org.apache.directory.server.core.api.CacheService;
import org.apache.directory.server.core.api.DnFactory;
import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
import org.apache.directory.server.core.partition.ldif.AbstractLdifPartition;
import org.apache.directory.server.core.partition.ldif.LdifPartition;
import org.apache.directory.server.core.partition.ldif.SingleFileLdifPartition;
import org.apache.directory.studio.apacheds.configuration.ApacheDS2ConfigurationPlugin;
import org.apache.directory.studio.apacheds.configuration.ApacheDS2ConfigurationPluginConstants;
import org.apache.directory.studio.apacheds.configuration.jobs.EntryBasedConfigurationPartition;
import org.apache.directory.studio.apacheds.configuration.jobs.PartitionsDiffComputer;
import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor;
import org.apache.directory.studio.common.ui.CommonUIUtils;
import org.apache.directory.studio.common.ui.filesystem.PathEditorInput;
import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry;
import org.apache.directory.studio.ldapbrowser.core.BrowserCorePlugin;
import org.apache.directory.studio.ldapbrowser.core.jobs.ExecuteLdifRunnable;
import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.part.FileEditorInput;
/**
* This class contains helpful methods for the Server Configuration Editor.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ServerConfigurationEditorUtils
{
/**
* Performs the "Save as..." action.
*
* @param monitor the monitor
* @param shell the shell
* @param input the editor input
* @param configWriter the configuration writer
* @param newInput a flag to indicate if a new input is required
* @return the new input for the editor
* @throws Exception
*/
public static IEditorInput saveAs( IProgressMonitor monitor, Shell shell, IEditorInput input,
ConfigWriter configWriter, Configuration configuration, boolean newInput )
throws Exception
{
// detect IDE or RCP:
// check if perspective org.eclipse.ui.resourcePerspective is available
boolean isIDE = CommonUIUtils.isIDEEnvironment();
if ( isIDE )
{
// Asking the user for the location where to 'save as' the file
final SaveAsDialog dialog = new SaveAsDialog( shell );
String inputClassName = input.getClass().getName();
if ( input instanceof FileEditorInput )
// FileEditorInput class is used when the file is opened
// from a project in the workspace.
{
dialog.setOriginalFile( ( ( FileEditorInput ) input ).getFile() );
}
else if ( input instanceof IPathEditorInput )
{
dialog.setOriginalFile( ResourcesPlugin.getWorkspace().getRoot()
.getFile( ( ( IPathEditorInput ) input ).getPath() ) );
}
else if ( inputClassName.equals( "org.eclipse.ui.internal.editors.text.JavaFileEditorInput" ) //$NON-NLS-1$
|| inputClassName.equals( "org.eclipse.ui.ide.FileStoreEditorInput" ) ) //$NON-NLS-1$
// The class 'org.eclipse.ui.internal.editors.text.JavaFileEditorInput'
// is used when opening a file from the menu File > Open... in Eclipse 3.2.x
// The class 'org.eclipse.ui.ide.FileStoreEditorInput' is used when
// opening a file from the menu File > Open... in Eclipse 3.3.x
{
dialog.setOriginalFile( ResourcesPlugin.getWorkspace().getRoot()
.getFile( new Path( input.getToolTipText() ) ) );
}
else
{
dialog.setOriginalName( ApacheDS2ConfigurationPluginConstants.CONFIG_LDIF );
}
// Open the dialog
if ( openDialogInUIThread( dialog ) != Dialog.OK )
{
return null;
}
// Getting if the resulting file
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile( dialog.getResult() );
// Creating the file if it does not exist
if ( !file.exists() )
{
file.create( new ByteArrayInputStream( "".getBytes() ), true, null ); //$NON-NLS-1$
}
// Creating the new input for the editor
FileEditorInput fei = new FileEditorInput( file );
// Saving the file to disk
File configFile = fei.getPath().toFile();
saveConfiguration( configFile, configWriter, configuration );
return fei;
}
else
{
boolean canOverwrite = false;
String path = null;
while ( !canOverwrite )
{
// Open FileDialog
path = openFileDialogInUIThread( shell );
if ( path == null )
{
return null;
}
// Check whether file exists and if so, confirm overwrite
final File externalFile = new File( path );
if ( externalFile.exists() )
{
String question = NLS.bind(
Messages.getString( "ServerConfigurationEditorUtils.TheFileAlreadyExistsWantToReplace" ), path ); //$NON-NLS-1$
MessageDialog overwriteDialog = new MessageDialog( shell,
Messages.getString( "ServerConfigurationEditorUtils.Question" ), null, question, //$NON-NLS-1$
MessageDialog.QUESTION, new String[]
{ IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 0 );
int overwrite = openDialogInUIThread( overwriteDialog );
switch ( overwrite )
{
case 0: // Yes
canOverwrite = true;
break;
case 1: // No
break;
case 2: // Cancel
default:
return null;
}
}
else
{
canOverwrite = true;
}
}
// Saving the file to disk
saveConfiguration( new File(path), configWriter, configuration );
// Checking if a new input is required
if ( newInput )
{
// Creating the new input for the editor
return new PathEditorInput( new Path( path ) );
}
else
{
return null;
}
}
}
/**
* Opens a {@link Dialog} in the UI thread.
*
* @param dialog the dialog
* @return the result of the dialog
*/
private static int openDialogInUIThread( final Dialog dialog )
{
// Defining our own encapsulating class for the result
class DialogResult
{
private int result;
public int getResult()
{
return result;
}
public void setResult( int result )
{
this.result = result;
}
}
// Creating an object to hold the result
final DialogResult result = new DialogResult();
// Opening the dialog in the UI thread
Display.getDefault().syncExec( new Runnable()
{
public void run()
{
result.setResult( dialog.open() );
}
} );
return result.getResult();
}
/**
* Opens a {@link FileDialog} in the UI thread.
*
* @param shell the shell
* @return the result of the dialog
*/
private static String openFileDialogInUIThread( final Shell shell )
{
// Defining our own encapsulating class for the result
class DialogResult
{
private String result;
public String getResult()
{
return result;
}
public void setResult( String result )
{
this.result = result;
}
}
// Creating an object to hold the result
final DialogResult result = new DialogResult();
// Opening the dialog in the UI thread
Display.getDefault().syncExec( new Runnable()
{
public void run()
{
FileDialog dialog = new FileDialog( shell, SWT.SAVE );
result.setResult( dialog.open() );
}
} );
return result.getResult();
}
/**
* Saves the configuration.
*
* @param input the connection server configuration input
* @param configWriter the configuration writer
* @param monitor the monitor
* @return <code>true</code> if the operation is successful, <code>false</code> if not
* @throws Exception
*/
public static void saveConfiguration( ConnectionServerConfigurationInput input, ConfigWriter configWriter,
IProgressMonitor monitor )
throws Exception
{
// Getting the original configuration partition
EntryBasedConfigurationPartition originalPartition = input.getOriginalPartition();
// Creating a new configuration partition
SchemaManager schemaManager = ApacheDS2ConfigurationPlugin.getDefault().getSchemaManager();
EntryBasedConfigurationPartition newconfigurationPartition = new EntryBasedConfigurationPartition(
schemaManager );
CacheService cacheService = new CacheService();
cacheService.initialize( null );
newconfigurationPartition.setCacheService( cacheService );
newconfigurationPartition.initialize();
List<LdifEntry> convertedLdifEntries = configWriter.getConvertedLdifEntries();
for ( LdifEntry ldifEntry : convertedLdifEntries )
{
newconfigurationPartition.addEntry( new DefaultEntry( schemaManager, ldifEntry.getEntry() ) );
}
// Suspends event firing in current thread.
ConnectionEventRegistry.suspendEventFiringInCurrentThread();
try
{
// Comparing both partitions to get the list of modifications to be applied
PartitionsDiffComputer partitionsDiffComputer = new PartitionsDiffComputer();
partitionsDiffComputer.setOriginalPartition( originalPartition );
partitionsDiffComputer.setDestinationPartition( newconfigurationPartition );
List<LdifEntry> modificationsList = partitionsDiffComputer.computeModifications( new String[]
{ SchemaConstants.ALL_USER_ATTRIBUTES } );
// Building the resulting LDIF
StringBuilder modificationsLdif = new StringBuilder();
for ( LdifEntry ldifEntry : modificationsList )
{
modificationsLdif.append( ldifEntry.toString() );
modificationsLdif.append( '\n' );
}
// Getting the browser connection associated with the connection
IBrowserConnection browserConnection = BrowserCorePlugin.getDefault().getConnectionManager()
.getBrowserConnection( input.getConnection() );
// Creating a StudioProgressMonitor to run the LDIF with
StudioProgressMonitor studioProgressMonitor = new StudioProgressMonitor( new NullProgressMonitor() );
// Updating the configuration with the resulting LDIF
ExecuteLdifRunnable.executeLdif( browserConnection, modificationsLdif.toString(), true, true,
studioProgressMonitor );
// Checking if there were errors during the execution of the LDIF
if ( studioProgressMonitor.errorsReported() )
{
throw new Exception(
Messages.getString( "ServerConfigurationEditorUtils.ChangesCouldNotBeSavedToConnection" ) ); //$NON-NLS-1$
}
else
{
// Swapping the new configuration partition
input.setOriginalPartition( newconfigurationPartition );
}
}
finally
{
// Resumes event firing in current thread.
ConnectionEventRegistry.resumeEventFiringInCurrentThread();
}
}
/**
* Saves the configuration.
*
* @param file the file
* @param configWriter the configuration writer
* @throws Exception
*/
public static void saveConfiguration( File file, ConfigWriter configWriter, Configuration configuration )
throws Exception
{
SchemaManager schemaManager = ApacheDS2ConfigurationPlugin.getDefault().getSchemaManager();
CacheService cacheService = new CacheService();
cacheService.initialize( null );
DnFactory dnFactory = null;
CsnFactory csnFactory = new CsnFactory( 0 );
if ( file != null )
{
// create partiton
AbstractLdifPartition configPartition;
if ( file.getName().equals( ApacheDS2ConfigurationPluginConstants.OU_CONFIG_LDIF ) )
{
File confDir = file.getParentFile();
File ouConfigLdifFile = new File( confDir, ApacheDS2ConfigurationPluginConstants.OU_CONFIG_LDIF );
File ouConfigDir = new File( confDir, ApacheDS2ConfigurationPluginConstants.OU_CONFIG );
if ( ouConfigLdifFile.exists() && ouConfigDir.exists() )
{
ouConfigLdifFile.delete();
FileUtils.deleteDirectory( ouConfigDir );
}
configPartition = createMultiFileConfiguration( confDir, schemaManager, dnFactory, cacheService );
}
else
{
if ( file.exists() )
{
file.delete();
}
configPartition = createSingleFileConfiguration( file, schemaManager, dnFactory, cacheService );
}
// write entries to partition
List<LdifEntry> convertedLdifEntries = configWriter.getConvertedLdifEntries();
for ( LdifEntry ldifEntry : convertedLdifEntries )
{
Entry entry = new DefaultEntry( schemaManager, ldifEntry.getEntry() );
if ( entry.get( SchemaConstants.ENTRY_CSN_AT ) == null )
{
entry.add( SchemaConstants.ENTRY_CSN_AT, csnFactory.newInstance().toString() );
}
if ( entry.get( SchemaConstants.ENTRY_UUID_AT ) == null )
{
String uuid = UUID.randomUUID().toString();
entry.add( SchemaConstants.ENTRY_UUID_AT, uuid );
}
configPartition.add( new AddOperationContext( null, entry ) );
}
}
}
private static SingleFileLdifPartition createSingleFileConfiguration( File configFile, SchemaManager schemaManager,
DnFactory dnFactory, CacheService cacheService ) throws Exception
{
SingleFileLdifPartition configPartition = new SingleFileLdifPartition( schemaManager, dnFactory );
configPartition.setId( "config" );
configPartition.setPartitionPath( configFile.toURI() );
configPartition.setSuffixDn( new Dn( schemaManager, "ou=config" ) );
configPartition.setSchemaManager( schemaManager );
configPartition.setCacheService( cacheService );
configPartition.initialize();
return configPartition;
}
private static LdifPartition createMultiFileConfiguration( File confDir, SchemaManager schemaManager, DnFactory dnFactory,
CacheService cacheService ) throws Exception
{
LdifPartition configPartition = new LdifPartition( schemaManager, dnFactory );
configPartition.setId( "config" );
configPartition.setPartitionPath( confDir.toURI() );
configPartition.setSuffixDn( new Dn( schemaManager, "ou=config" ) );
configPartition.setSchemaManager( schemaManager );
configPartition.setCacheService( cacheService );
configPartition.initialize();
return configPartition;
}
// TODO: something link this should be used in future to only write changes to partition
private static List<LdifEntry> computeModifications( ConfigWriter configWriter,
AbstractBTreePartition originalPartition ) throws Exception
{
// Creating a new configuration partition
SchemaManager schemaManager = ApacheDS2ConfigurationPlugin.getDefault().getSchemaManager();
EntryBasedConfigurationPartition newconfigurationPartition = new EntryBasedConfigurationPartition(
schemaManager );
CacheService cacheService = new CacheService();
cacheService.initialize( null );
newconfigurationPartition.setCacheService( cacheService );
newconfigurationPartition.initialize();
List<LdifEntry> convertedLdifEntries = configWriter.getConvertedLdifEntries();
for ( LdifEntry ldifEntry : convertedLdifEntries )
{
newconfigurationPartition.addEntry( new DefaultEntry( schemaManager, ldifEntry.getEntry() ) );
}
// Comparing both partitions to get the list of modifications to be applied
PartitionsDiffComputer partitionsDiffComputer = new PartitionsDiffComputer();
partitionsDiffComputer.setOriginalPartition( originalPartition );
partitionsDiffComputer.setDestinationPartition( newconfigurationPartition );
List<LdifEntry> modificationsList = partitionsDiffComputer.computeModifications( new String[]
{ SchemaConstants.ALL_USER_ATTRIBUTES } );
return modificationsList;
}
/**
* Checks if the string is <code>null</code>
* and returns an empty string in that case.
*
* @param s the string
* @return a non-<code>null</code> string
*/
public static String checkNull( String s )
{
if ( s == null )
{
return "";
}
return s;
}
/**
* Checks if the string is <code>null</code>
* and returns an empty string in that case.
*
* @param s the string
* @return a non-<code>null</code> string
*/
public static String checkEmptyString( String s )
{
if ( "".equals( s ) )
{
return null;
}
return s;
}
}