blob: 4381ff05f22ba296bba156b96e0a06e2b22e279a [file] [log] [blame]
/*
* 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.ldapbrowser.core.jobs;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.directory.api.dsmlv2.DsmlDecorator;
import org.apache.directory.api.dsmlv2.request.AddRequestDsml;
import org.apache.directory.api.dsmlv2.request.BatchRequestDsml;
import org.apache.directory.api.dsmlv2.response.BatchResponseDsml;
import org.apache.directory.api.dsmlv2.response.SearchResponseDsml;
import org.apache.directory.api.dsmlv2.response.SearchResultDoneDsml;
import org.apache.directory.api.dsmlv2.response.SearchResultEntryDsml;
import org.apache.directory.api.dsmlv2.response.SearchResultReferenceDsml;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
import org.apache.directory.api.ldap.model.message.LdapResult;
import org.apache.directory.api.ldap.model.message.MessageTypeEnum;
import org.apache.directory.api.ldap.model.message.Response;
import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
import org.apache.directory.api.ldap.model.message.SearchResultDone;
import org.apache.directory.api.ldap.model.message.SearchResultDoneImpl;
import org.apache.directory.api.ldap.model.url.LdapUrl;
import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor;
import org.apache.directory.studio.connection.core.Connection;
import org.apache.directory.studio.connection.core.io.api.StudioSearchResultEnumeration;
import org.apache.directory.studio.connection.core.jobs.StudioConnectionRunnableWithProgress;
import org.apache.directory.studio.ldapbrowser.core.BrowserCoreMessages;
import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
import org.apache.directory.studio.ldapbrowser.core.model.SearchParameter;
import org.apache.directory.studio.ldapbrowser.core.utils.JNDIUtils;
/**
* Runnable for Exporting a part of a LDAP Server into a DSML File.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ExportDsmlRunnable implements StudioConnectionRunnableWithProgress
{
private static final String OBJECTCLASS_OBJECTCLASS_OID = "objectClass"; //$NON-NLS-1$
private static final String OBJECTCLASS_OBJECTCLASS_NAME = "2.5.4.0"; //$NON-NLS-1$
private static final String REFERRAL_OBJECTCLASS_OID = "2.16.840.1.113730.3.2.6"; //$NON-NLS-1$
private static final String REFERRAL_OBJECTCLASS_NAME = "referral"; //$NON-NLS-1$
private static final String REF_ATTRIBUTETYPE_OID = "2.16.840.1.113730.3.1.34"; //$NON-NLS-1$
private static final String REF_ATTRIBUTETYPE_NAME = "ref"; //$NON-NLS-1$
/** The name of the DSML file to export to */
private String exportDsmlFilename;
/** The connection to use */
private IBrowserConnection browserConnection;
/** The Search Parameter of the export*/
private SearchParameter searchParameter;
/** The type of the export */
private ExportDsmlJobType type = ExportDsmlJobType.RESPONSE;
/**
* The LDAP Codec - for now need by the DSML Parser
* @TODO - this should be removed - no reason why the DSML parser needs it
* @TODO - hate to make it static like this but methods are static
*/
private static LdapApiService codec = LdapApiServiceFactory.getSingleton();
/**
* This enum contains the two possible export types.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public enum ExportDsmlJobType
{
RESPONSE, REQUEST
}
/**
* Creates a new instance of ExportDsmlRunnable.
*
* @param exportDsmlFilename
* the name of the DSML file to export to
* @param connection
* the connection to use
* @param searchParameter
* the Search Parameter of the export
*/
public ExportDsmlRunnable( String exportDsmlFilename, IBrowserConnection connection,
SearchParameter searchParameter, ExportDsmlJobType type )
{
this.exportDsmlFilename = exportDsmlFilename;
this.browserConnection = connection;
this.searchParameter = searchParameter;
this.type = type;
// Adding the name and OID of the 'ref' attribute to the list of returning attributes
// for handling externals correctly
List<String> returningAttributes = new ArrayList<>( Arrays.asList( searchParameter
.getReturningAttributes() ) );
returningAttributes.add( REF_ATTRIBUTETYPE_NAME );
returningAttributes.add( REF_ATTRIBUTETYPE_OID );
searchParameter.setReturningAttributes( returningAttributes.toArray( new String[0] ) );
}
/**
* {@inheritDoc}
*/
public Connection[] getConnections()
{
return new Connection[]
{ browserConnection.getConnection() };
}
/**
* {@inheritDoc}
*/
public String getName()
{
return BrowserCoreMessages.jobs__export_dsml_name;
}
/**
* {@inheritDoc}
*/
public Object[] getLockedObjects()
{
List<String> l = new ArrayList<>();
l.add( browserConnection.getUrl() + "_" + DigestUtils.shaHex( exportDsmlFilename ) ); //$NON-NLS-1$
return l.toArray();
}
/**
* {@inheritDoc}
*/
public String getErrorMessage()
{
return BrowserCoreMessages.jobs__export_dsml_error;
}
/**
* {@inheritDoc}
*/
public void run( StudioProgressMonitor monitor )
{
monitor.beginTask( BrowserCoreMessages.jobs__export_dsml_task, 4 );
monitor.reportProgress( " " ); //$NON-NLS-1$
monitor.worked( 1 );
try
{
// Creating a dummy monitor that will be used to check if something
// went wrong when executing the request
StudioProgressMonitor dummyMonitor = new StudioProgressMonitor( monitor );
// Searching for the requested entries
StudioSearchResultEnumeration ne = SearchRunnable.search( browserConnection, searchParameter, dummyMonitor );
monitor.worked( 1 );
// Getting the DSML string associated to the search
// and the type of answer the user is expecting
String dsmlExportString = null;
switch ( type )
{
case RESPONSE:
dsmlExportString = processAsDsmlResponse( ne, dummyMonitor );
break;
case REQUEST:
dsmlExportString = processAsDsmlRequest( ne, dummyMonitor );
break;
}
monitor.worked( 1 );
// Writing the DSML string to the final destination file.
if ( dsmlExportString != null )
{
try ( FileOutputStream fos = new FileOutputStream( exportDsmlFilename ) )
{
try ( OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF-8" ) ) //$NON-NLS-1$
{
try ( BufferedWriter bufferedWriter = new BufferedWriter( osw ) )
{
bufferedWriter.write( dsmlExportString );
}
}
}
}
monitor.worked( 1 );
}
catch ( Exception e )
{
monitor.reportError( e );
}
}
/**
* Processes the {@link StudioSearchResultEnumeration} as a DSML response.
*
* @param sre the search result enumeration
* @param monitor the monitor
* @return the associated DSML
* @throws LdapException
*/
private String processAsDsmlResponse( StudioSearchResultEnumeration sre, StudioProgressMonitor monitor ) throws LdapException
{
// Creating the batch reponse
BatchResponseDsml batchResponse = new BatchResponseDsml();
processAsDsmlResponse( sre, batchResponse, monitor, searchParameter );
// Returning the associated DSML
return batchResponse.toDsml();
}
/**
* Processes the {@link StudioSearchResultEnumeration} as a DSML response.
*
* @param sre
* the search result enumeration
* @param monitor
* the monitor
* @param searchParameter
* the search parameter
* @throws LdapURLEncodingException
* @throws org.apache.directory.api.ldap.model.exception.LdapException
*/
public static void processAsDsmlResponse( StudioSearchResultEnumeration sre, BatchResponseDsml batchResponse,
StudioProgressMonitor monitor, SearchParameter searchParameter ) throws LdapException
{
// Creating and adding the search response
SearchResponseDsml sr = new SearchResponseDsml( codec );
batchResponse.addResponse( sr );
try
{
int count = 0;
if ( !monitor.errorsReported() )
{
// Creating and adding a search result entry or reference for each result
while ( sre.hasMore() )
{
Entry entry = sre.next().getEntry();
sr.addResponse( convertSearchResultToDsml( entry ) );
count++;
monitor.reportProgress( BrowserCoreMessages.bind( BrowserCoreMessages.jobs__export_progress,
new String[]
{ Integer.toString( count ) } ) );
}
}
}
catch ( LdapException e )
{
int ldapStatusCode = JNDIUtils.getLdapStatusCode( e );
if ( ldapStatusCode == 3 || ldapStatusCode == 4 || ldapStatusCode == 11 )
{
// ignore
}
else
{
monitor.reportError( e );
}
}
// Creating and adding a search result done at the end of the results
SearchResultDone srd = new SearchResultDoneImpl();
LdapResult ldapResult = srd.getLdapResult();
if ( !monitor.errorsReported() )
{
ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
}
else
{
// Getting the exception
Throwable t = monitor.getException();
// Setting the result code
ldapResult.setResultCode( ResultCodeEnum.getBestEstimate( t, MessageTypeEnum.SEARCH_REQUEST ) );
// Setting the error message if there's one
if ( t.getMessage() != null )
{
ldapResult.setDiagnosticMessage( t.getMessage() );
}
}
sr.addResponse( new SearchResultDoneDsml( codec, srd ) );
}
/**
* Converts the given {@link SearchResult} to a {@link SearchResultEntryDsml}.
*
* @param entry2 the search result
* @return the associated search result entry DSML
* @throws org.apache.directory.api.ldap.model.exception.LdapException
*/
private static DsmlDecorator<? extends Response> convertSearchResultToDsml( Entry entry )
throws LdapException
{
if ( isReferral( entry ) )
{
// The search result is a referral
SearchResultReferenceDsml srr = new SearchResultReferenceDsml( codec );
// Getting the 'ref' attribute
Attribute refAttribute = entry.get( ExportDsmlRunnable.REF_ATTRIBUTETYPE_NAME );
if ( refAttribute == null )
{
// If we did not get it by its name, let's get it by its OID
refAttribute = entry.get( ExportDsmlRunnable.REF_ATTRIBUTETYPE_OID );
}
// Adding references
if ( refAttribute != null )
{
for ( Value value : refAttribute )
{
srr.addSearchResultReference( new LdapUrl( ( String ) value.getString() ) );
}
}
return srr;
}
else
{
// The search result is NOT a referral
SearchResultEntryDsml sre = new SearchResultEntryDsml( codec );
sre.setEntry( entry );
return sre;
}
}
/**
* Indicates if the given entry is a referral.
*
* @param entry
* the entry
* @return
* <code>true</code> if the given entry is a referral, <code>false</code> if not
*/
private static boolean isReferral( Entry entry )
{
if ( entry != null )
{
// Getting the 'objectClass' Attribute
Attribute objectClassAttribute = entry.get( ExportDsmlRunnable.OBJECTCLASS_OBJECTCLASS_NAME );
if ( objectClassAttribute == null )
{
objectClassAttribute = entry.get( ExportDsmlRunnable.OBJECTCLASS_OBJECTCLASS_OID );
}
if ( objectClassAttribute != null )
{
// Checking if the 'objectClass' attribute contains the
// 'referral' object class as value
return ( ( objectClassAttribute.contains( ExportDsmlRunnable.REFERRAL_OBJECTCLASS_NAME ) ) || ( objectClassAttribute
.contains( ExportDsmlRunnable.REFERRAL_OBJECTCLASS_OID ) ) );
}
}
return false;
}
/**
* Processes the {@link StudioSearchResultEnumeration} as a DSML request.
*
* @param sre
* the search result enumeration
* @param monitor
* the monitor
* @return
* the associated DSML
* @throws LdapException
*/
private String processAsDsmlRequest( StudioSearchResultEnumeration sre, StudioProgressMonitor monitor )
throws LdapException
{
// Creating the batch request
BatchRequestDsml batchRequest = new BatchRequestDsml();
try
{
int count = 0;
if ( !monitor.errorsReported() )
{
// Creating and adding an add request for each result
while ( sre.hasMore() )
{
Entry entry = sre.next().getEntry();
AddRequestDsml arDsml = convertToAddRequestDsml( entry );
batchRequest.addRequest( arDsml );
count++;
monitor.reportProgress( BrowserCoreMessages.bind( BrowserCoreMessages.jobs__export_progress,
new String[]
{ Integer.toString( count ) } ) );
}
}
}
catch ( LdapException e )
{
int ldapStatusCode = JNDIUtils.getLdapStatusCode( e );
if ( ldapStatusCode == 3 || ldapStatusCode == 4 || ldapStatusCode == 11 )
{
// ignore
}
else
{
monitor.reportError( e );
}
}
// Returning the associated DSML
return batchRequest.toDsml();
}
/**
* Converts the given {@link SearchResult} to an {@link AddRequestDsml}.
*
* @param entry2
* the {@link SearchResult}
* @return
* the associated {@link AddRequestDsml}
* @throws LdapException
*/
private AddRequestDsml convertToAddRequestDsml( Entry entry )
throws LdapException
{
AddRequestDsml ar = new AddRequestDsml( codec );
ar.setEntry( entry );
return ar;
}
}