/******************************************************************************
* Copyright (c) 2009-2013, Linagora
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Linagora - initial API and implementation
*******************************************************************************/
package com.ebmwebsourcing.petals.services.explorer.sources;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import com.ebmwebsourcing.petals.common.internal.provisional.emf.InvalidJbiXmlException;
import com.ebmwebsourcing.petals.common.internal.provisional.utils.CommonUtils;
import com.ebmwebsourcing.petals.common.internal.provisional.utils.IoUtils;
import com.ebmwebsourcing.petals.common.internal.provisional.utils.JbiXmlUtils;
import com.ebmwebsourcing.petals.services.PetalsServicesPlugin;
import com.ebmwebsourcing.petals.services.explorer.model.ServiceUnitBean;
import com.sun.java.xml.ns.jbi.Jbi;
import com.sun.java.xml.ns.jbi.ServiceUnit;
import com.sun.java.xml.ns.jbi.Target;
/**
* A source looking for end-points in a directory of zipped service assemblies.
* @author Vincent Zurczak - EBM WebSourcing
*/
public class SaDirectorySource extends EndpointSource {
private final boolean unzipAll, showConsumes;
private final File directoryOrSaFile;
private final Map<ServiceUnitBean,String> suBeanToSaFile = new HashMap<ServiceUnitBean,String>( 5 );
private final Map<ServiceUnitBean,File> suBeanToExplodedSu = new HashMap<ServiceUnitBean,File>( 5 );
private final List<File> filesToDelete = new ArrayList<File> ();
/**
* Constructor.
* @param directoryOrSaFile
* @param unzipAll true to unzip all the SU, false to unzip just the jbi.xml
* @param showConsumes
*/
public SaDirectorySource( File directoryOrSaFile, boolean unzipAll, boolean showConsumes ) {
super( directoryOrSaFile.getName(), directoryOrSaFile.getAbsolutePath());
this.unzipAll = unzipAll;
this.showConsumes = showConsumes;
this.directoryOrSaFile = directoryOrSaFile;
if( directoryOrSaFile.isDirectory())
this.description = "The Petals services contained in service assemblies at " + this.directoryOrSaFile.getAbsolutePath() + ".";
else
this.description = "The Petals services contained in the service assembly at " + this.directoryOrSaFile.getAbsolutePath() + ".";
}
/**
* @return the directory
*/
public File getDirectoryOrSaFile() {
return this.directoryOrSaFile;
}
/*
* (non-Javadoc)
* @see com.ebmwebsourcing.petals.services.explorer.sources.EndpointSource
* #dispose()
*/
@Override
public void dispose() {
// Delete exploded files
try {
File[] ftd = new File[ this.suBeanToExplodedSu.size()];
IoUtils.deleteFilesRecursively( this.suBeanToExplodedSu.values().toArray( ftd ));
ftd = new File[ this.filesToDelete.size()];
IoUtils.deleteFilesRecursively( this.filesToDelete.toArray( ftd ));
} catch( IOException e ) {
PetalsServicesPlugin.log( e, IStatus.WARNING );
}
}
/*
* (non-Javadoc)
* @see com.ebmwebsourcing.petals.services.explorer.sources.EndpointSource
* #refreshServiceUnits(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected Collection<ServiceUnitBean> refreshServiceUnits( IProgressMonitor monitor ) {
if( monitor == null )
monitor = new NullProgressMonitor();
dispose();
this.suBeanToSaFile.clear();
this.suBeanToExplodedSu.clear();
// Get SAs
FileFilter filter = new FileFilter() {
@Override
public boolean accept( File pathname ) {
return pathname.getName().endsWith( ".zip" );
}
};
File[] saFiles;
if( this.directoryOrSaFile.isDirectory())
saFiles = this.directoryOrSaFile.listFiles( filter );
else
saFiles = new File[] { this.directoryOrSaFile };
// Unzip it all
for( File saFile : saFiles ) {
ZipFile zipFile = null;
try {
zipFile = new ZipFile( saFile );
ZipEntry jbiXmlEntry = zipFile.getEntry( "META-INF/jbi.xml" );
if( jbiXmlEntry == null )
continue; // Not a SA
monitor.worked( 1 );
// Get the content of the jbi.xml for the SA
File tempSaFile = File.createTempFile( "tempSaJbiXml", ".xml" );
// Safety measure
tempSaFile.deleteOnExit();
this.filesToDelete.add( tempSaFile );
InputStream is = zipFile.getInputStream( jbiXmlEntry );
IoUtils.copyStream( is, tempSaFile );
is.close ();
Map<String,String> suZipNameToComponentName = new HashMap<String,String> ();
String saId = null;
try {
Jbi jbi = JbiXmlUtils.getJbiXmlModel( tempSaFile );
if( jbi != null && jbi.getServiceAssembly() != null ) {
if( jbi.getServiceAssembly().getIdentification() != null )
saId = jbi.getServiceAssembly().getIdentification().getName();
EList<ServiceUnit> sus = jbi.getServiceAssembly().getServiceUnit();
for( ListIterator<ServiceUnit> it = sus.listIterator(); it.hasNext(); ) {
Target target = it.next().getTarget();
suZipNameToComponentName.put( target.getArtifactsZip().trim(), target.getComponentName().trim());
}
}
} catch( InvalidJbiXmlException e1 ) {
PetalsServicesPlugin.log( e1, IStatus.WARNING, "Invalid jbi.xml for the SA " + saFile.getAbsolutePath());
}
// Get SU entries
Enumeration<?> entries = zipFile.entries();
while( entries.hasMoreElements()) {
ZipEntry saEntry = (ZipEntry) entries.nextElement();
if( ! saEntry.getName().endsWith( ".zip" ))
continue;
File tmpDir = new File(
System.getProperty( "java.io.tmpdir" ),
"Petals-" + UUID.randomUUID().toString());
if( ! tmpDir.mkdir())
throw new IOException( "Could not create a temporary directory." );
this.filesToDelete.add( tmpDir );
InputStream tempIn = zipFile.getInputStream( saEntry );
ZipInputStream in = new ZipInputStream( tempIn );
try {
ZipEntry suEntry;
while(( suEntry = in.getNextEntry()) != null ) {
// Only watch the jbi.xml file?
if( ! this.unzipAll
&& ! "META-INF/jbi.xml".equals( suEntry.getName()))
continue;
// Extract the jbi.xml file and create a ServiceUnitBean instance
File tempSuFile = new File( tmpDir, suEntry.getName());
// Safety measure
tempSuFile.deleteOnExit();
if( ! tempSuFile.getParentFile().exists()
&& ! tempSuFile.getParentFile().mkdirs())
throw new IOException( "Could not create the parent of a temporary file." );
if( tempSuFile.getParentFile().exists()
&& ! tempSuFile.getParentFile().isDirectory())
throw new IOException( "The parent of a temporary file is not a directory, as expected." );
if( suEntry.isDirectory()) {
if( ! tempSuFile.mkdir())
throw new IOException( "Could not create a temporary directory." );
// No need to go further
continue;
} else if( ! tempSuFile.createNewFile()) {
throw new IOException( "Could not create a temporary file." );
}
IoUtils.copyStream( in, tempSuFile );
if( ! "jbi.xml".equalsIgnoreCase( tempSuFile.getName()))
continue;
String suName = new Path( saEntry.getName()).removeFileExtension().lastSegment();
try {
Jbi jbi = JbiXmlUtils.getJbiXmlModel( tempSuFile );
ServiceUnitBean suBean = new ServiceUnitBean();
suBean.setSource( this );
suBean.setServiceAssemblyId( saId == null ? "" : saId );
suBean.setJbiXmlLocation( tempSuFile.getAbsolutePath());
suBean.setSaJbiXmlLocation( tempSaFile.getAbsolutePath());
suBean.setServiceUnitName( suName );
String componentName = suZipNameToComponentName.get( saEntry.getName());
if( componentName != null ) {
componentName = new Path( componentName ).lastSegment();
suBean.setComponentName( componentName );
}
getEndpointBeans( jbi, suBean );
if( this.showConsumes || suBean.getEndpoints().size() > 0 )
this.suBeanToSaFile.put( suBean, saFile.getAbsolutePath());
} catch( InvalidJbiXmlException e ) {
PetalsServicesPlugin.log(
e, IStatus.WARNING,
"Invalid jbi.xml for the SU " + suName + " in " + saFile.getAbsolutePath());
}
// Do not loop anymore
if( ! this.unzipAll )
break;
}
} finally {
in.close();
tempIn.close();
}
}
} catch( FileNotFoundException e ) {
PetalsServicesPlugin.log( e, IStatus.WARNING );
} catch( IOException e ) {
PetalsServicesPlugin.log( e, IStatus.WARNING );
} finally {
// Close the ZIP file
if( zipFile != null ) {
try {
zipFile.close();
} catch( IOException e ) {
PetalsServicesPlugin.log( e, IStatus.ERROR );
}
}
}
}
return this.suBeanToSaFile.keySet();
}
/**
* A class used to unzip a SA and showing a progress bar in the Eclipse UI.
*/
private class UnzippingRunnableWithProgress implements IRunnableWithProgress {
private final ServiceUnitBean suBean;
/**
* Constructor.
* @param suBean
*/
UnzippingRunnableWithProgress( ServiceUnitBean suBean ) {
this.suBean = suBean;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.operation.IRunnableWithProgress
* #run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void run( IProgressMonitor monitor )
throws InvocationTargetException, InterruptedException {
ZipFile zipFile = null;
try {
monitor.beginTask( "Unzipping the service assembly...", IProgressMonitor.UNKNOWN );
Map<ServiceUnitBean, File> localSuBeanToExplodedSu = new HashMap<ServiceUnitBean, File>();
this.suBean.setWsdlContainerLocationComputed( true );
// Get the SA and check if it still exists
String saFileLocation = SaDirectorySource.this.suBeanToSaFile.get( this.suBean );
File saFile;
if( saFileLocation == null
|| ( saFile = new File( saFileLocation )) == null )
return;
// Unzip all the SUs
zipFile = new ZipFile( saFile );
Enumeration<?> entries = zipFile.entries();
while( entries.hasMoreElements()) {
// In case of cancellation, remove all the extracted files of this SA
if( monitor.isCanceled()) {
File[] ftd = new File[ localSuBeanToExplodedSu.size()];
try {
IoUtils.deleteFilesRecursively( localSuBeanToExplodedSu.values().toArray( ftd ));
this.suBean.setWsdlContainerLocationComputed( false );
} catch( IOException e ) {
PetalsServicesPlugin.log( e, IStatus.WARNING );
}
return;
}
// Read the next entry
ZipEntry saEntry = (ZipEntry) entries.nextElement();
if( ! saEntry.getName().endsWith( ".zip" ))
continue;
monitor.worked( 1 );
monitor.subTask( "Extracting " + saEntry.getName() + "..." );
// Create a temporary folder to store the SU
File tempDir = new File(
System.getProperty( "java.io.tmpdir" ),
UUID.randomUUID().toString());
if( ! tempDir.mkdir()) {
PetalsServicesPlugin.log(
"The service unit " + saEntry.getName() + " could not be extracted from the service assembly.",
IStatus.ERROR );
return;
}
localSuBeanToExplodedSu.put( this.suBean, tempDir );
// Unzip the SU
InputStream tempIn = zipFile.getInputStream( saEntry );
ZipInputStream in = new ZipInputStream( tempIn );
ZipEntry suEntry;
while(( suEntry = in.getNextEntry()) != null ) {
File tempFile = new File( tempDir, suEntry.getName());
if( ! tempFile.exists()) {
File parent = tempFile.getParentFile();
if( ! parent.exists() && ! parent.mkdirs())
continue;
if( tempFile.createNewFile())
IoUtils.copyStream( in, tempFile );
}
}
in.close();
tempIn.close();
SaDirectorySource.this.suBeanToExplodedSu.putAll( localSuBeanToExplodedSu );
}
} catch( ZipException e ) {
PetalsServicesPlugin.log( e, IStatus.ERROR );
} catch( IOException e ) {
PetalsServicesPlugin.log( e, IStatus.ERROR );
} finally {
if( zipFile != null ) {
try {
zipFile.close();
} catch( IOException e ) {
// nothing
}
}
monitor.done();
}
}
}
/*
* (non-Javadoc)
* @see com.ebmwebsourcing.petals.services.explorer.sources.EndpointSource
* #getWsdlContainerLocation(com.ebmwebsourcing.petals.services.explorer.model.ServiceUnitBean)
*/
@Override
public String getWsdlContainerLocation( ServiceUnitBean suBean ) {
// Need to unzip?
if( ! this.suBeanToExplodedSu.containsKey( suBean )) {
IProgressService ps = PlatformUI.getWorkbench().getProgressService();
try {
ps.busyCursorWhile( new UnzippingRunnableWithProgress( suBean ));
} catch( InvocationTargetException e ) {
PetalsServicesPlugin.log( e, IStatus.ERROR );
} catch( InterruptedException e ) {
// nothing
}
}
// The SU must have been extracted
File explodedSuFile = this.suBeanToExplodedSu.get( suBean );
if( explodedSuFile == null || ! explodedSuFile.exists())
return "";
return explodedSuFile.getAbsolutePath();
}
/*
* (non-Javadoc)
* @see com.ebmwebsourcing.petals.services.explorer.sources.EndpointSource
* #equals(java.lang.Object)
*/
@Override
public boolean equals( Object obj ) {
return obj instanceof SaDirectorySource
&& CommonUtils.areEqual( this.directoryOrSaFile, ((SaDirectorySource) obj).directoryOrSaFile );
}
/*
* (non-Javadoc)
* @see com.ebmwebsourcing.petals.services.explorer.sources.EndpointSource
* #hashCode()
*/
@Override
public int hashCode() {
return this.directoryOrSaFile != null ? this.directoryOrSaFile.hashCode() : 11;
}
}