/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2016 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.marketplace.domain.model.entities.serialization.jaxb;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.marketplace.domain.model.entities.interfaces.ICategory;
import org.pentaho.marketplace.domain.model.entities.serialization.jaxb.dto.Market;
import org.pentaho.marketplace.domain.model.entities.serialization.jaxb.dto.Version;
import org.pentaho.marketplace.domain.model.entities.DevelopmentStage;
import org.pentaho.marketplace.domain.model.entities.MarketEntryType;
import org.pentaho.marketplace.domain.model.entities.interfaces.IDevelopmentStage;
import org.pentaho.marketplace.domain.model.entities.interfaces.IPlugin;
import org.pentaho.marketplace.domain.model.entities.interfaces.IPluginVersion;
import org.pentaho.marketplace.domain.model.entities.serialization.IMarketplaceXmlSerializer;
import org.pentaho.marketplace.domain.model.factories.interfaces.ICategoryFactory;
import org.pentaho.marketplace.domain.model.factories.interfaces.IPluginFactory;
import org.pentaho.marketplace.domain.model.factories.interfaces.IPluginVersionFactory;
import org.pentaho.marketplace.util.XmlParserFactoryProducer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import javax.xml.xpath.XPathExpressionException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class JAXBMarketplaceXmlSerializer implements IMarketplaceXmlSerializer {
// region Constants
private static final String OSGI_URL_SUFFIX = ".kar";
// endregion
// region Properties
private JAXBContext jaxbContext;
private Unmarshaller jaxbUnmarshaller;
private ICategoryFactory categoryFactory;
private IPluginFactory pluginFactory;
private IPluginVersionFactory pluginVersionFactory;
protected Log getLogger() {
return this.logger;
}
private Log logger = LogFactory.getLog( this.getClass() );
// endregion
// region Constructors
public JAXBMarketplaceXmlSerializer( IPluginFactory pluginFactory,
IPluginVersionFactory pluginVersionFactory,
ICategoryFactory categoryFactory ) {
this.pluginFactory = pluginFactory;
this.pluginVersionFactory = pluginVersionFactory;
this.categoryFactory = categoryFactory;
try {
this.jaxbContext = JAXBContext.newInstance( Market.class );
this.jaxbUnmarshaller = jaxbContext.createUnmarshaller();
} catch ( JAXBException e ) {
this.getLogger().error( "Error initializing JAXBMarketplaceXmlSerializer.", e );
}
}
// endregion
// regions Methods
@Override public Map<String, IPlugin> getPlugins( InputStream xmlInputStream ) {
try {
SAXParserFactory spf = XmlParserFactoryProducer.createSecureSAXParserFactory();
Source xmlSource = new SAXSource( spf.newSAXParser().getXMLReader(), new InputSource( xmlInputStream ) );
Market market = (Market) jaxbUnmarshaller.unmarshal( xmlSource );
Map<String, IPlugin> plugins = this.toPlugins( market );
return plugins;
} catch ( JAXBException | SAXException | ParserConfigurationException e ) {
this.getLogger().debug( "Failed trying to parse invalid marketplace metadata." );
return Collections.emptyMap();
}
}
@Override public Map<String, IPlugin> getPlugins( String xml ) {
try {
SAXParserFactory spf = XmlParserFactoryProducer.createSecureSAXParserFactory();
Source xmlSource = new SAXSource( spf.newSAXParser().getXMLReader(), new InputSource( new StringReader( xml ) ) );
Market market = (Market) jaxbUnmarshaller.unmarshal( xmlSource );
Map<String, IPlugin> plugins = this.toPlugins( market );
return plugins;
} catch ( JAXBException | SAXException | ParserConfigurationException e ) {
this.getLogger().debug( "Failed trying to parse invalid marketplace metadata." );
return Collections.emptyMap();
}
}
@Override public Map<String, IPlugin> getPlugins( Document marketplaceMetadataDocument )
throws XPathExpressionException {
try {
Market market = (Market) jaxbUnmarshaller.unmarshal( marketplaceMetadataDocument );
Map<String, IPlugin> plugins = this.toPlugins( market );
return plugins;
} catch ( JAXBException e ) {
this.getLogger().debug( "Failed trying to parse invalid marketplace metadata." );
return Collections.emptyMap();
}
}
@Override public IPluginVersion getInstalledVersion( String xml ) {
return this.getInstalledVersion( new InputSource( new StringReader( xml ) ) );
}
@Override public IPluginVersion getInstalledVersion( InputSource inputDocument ) {
IPluginVersion version = null;
try {
DocumentBuilderFactory dbf = XmlParserFactoryProducer.createSecureDocBuilderFactory();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse( inputDocument );
version = this.getInstalledVersion( document );
} catch ( Exception e ) {
this.getLogger().error( "Error getting plugin version from version xml.", e );
// TODO: throw app exception in order to return error in endpoint
}
return version;
}
@Override public IPluginVersion getInstalledVersion( Document installedVersionDocument ) {
NodeList versionElements = installedVersionDocument.getElementsByTagName( "version" );
if ( versionElements.getLength() == 0 ) {
return null;
}
Element versionElement = (Element) versionElements.item( 0 );
IPluginVersion version = this.pluginVersionFactory.create();
version.setBranch( versionElement.getAttribute( "branch" ) );
version.setVersion( versionElement.getTextContent() );
version.setBuildId( versionElement.getAttribute( "buildId" ) );
return version;
}
// region DTO => Entity mapping
private Map<String, IPlugin> toPlugins( Market market ) {
Collection<Market.MarketEntry> marketEntries = market.getMarketEntry();
Map<String, IPlugin> plugins = new HashMap<>( marketEntries.size() );
int rank = 0;
for ( Market.MarketEntry entry : marketEntries ) {
plugins.put( entry.getId(), this.toPlugin( entry, rank++ ) );
}
return plugins;
}
private IPlugin toPlugin( Market.MarketEntry entry, int rank ) {
IPlugin plugin = this.pluginFactory.create();
//fill the instance
plugin.setRank( rank );
plugin.setId( entry.getId() );
plugin.setName( entry.getName() );
plugin.setType( this.toEntryType( entry.getType() ) );
plugin.setImg( entry.getImg() );
plugin.setSmallImg( entry.getSmallImg() );
plugin.setDocumentationUrl( entry.getDocumentationUrl() );
plugin.setDescription( entry.getDescription() );
plugin.setAuthorName( entry.getAuthor() );
plugin.setAuthorUrl( entry.getAuthorUrl() );
plugin.setAuthorLogo( entry.getAuthorLogo() );
plugin.setInstallationNotes( entry.getInstallationNotes() );
plugin.setDependencies( entry.getDependencies() );
plugin.setLicense( entry.getLicense() );
plugin.setLicenseName( entry.getLicenseName() );
plugin.setLicenseText( entry.getLicenseText() );
List<Version> versions = ( entry.getVersions() != null )
? entry.getVersions().getVersion()
: new ArrayList<Version>( 0 );
plugin.setVersions( this.toVersions( versions ) );
List<String> screenshots = ( entry.getScreenshots() != null )
? entry.getScreenshots().getScreenshot()
: new ArrayList<String>( 0 );
plugin.setScreenshots( screenshots.toArray( new String[ screenshots.size() ] ) );
plugin.setCategory( this.toCategory( entry.getCategory() ) );
return plugin;
}
private Collection<IPluginVersion> toVersions( List<Version> versions ) {
Collection<IPluginVersion> pluginVersions = new ArrayList<>( versions.size() );
for ( Version version : versions ) {
pluginVersions.add( this.toVersion( version ) );
}
return pluginVersions;
}
private IPluginVersion toVersion( Version version ) {
//get new pluginVersion instance
IPluginVersion pluginVersion = this.pluginVersionFactory.create();
//fill the instance
pluginVersion.setBranch( version.getBranch() );
pluginVersion.setName( version.getName() );
String downloadUrl = version.getPackageUrl();
if ( downloadUrl != null ) {
downloadUrl = downloadUrl.trim();
pluginVersion.setDownloadUrl( downloadUrl );
pluginVersion.setIsOsgi( downloadUrl.endsWith( OSGI_URL_SUFFIX ) );
}
pluginVersion.setVersion( version.getVersion() );
pluginVersion.setSamplesDownloadUrl( version.getSamplesUrl() );
pluginVersion.setDescription( version.getDescription() );
pluginVersion.setChangelog( version.getChangelog() );
pluginVersion.setBuildId( version.getBuildId() );
//pluginVersion.setReleaseDate( version.getReleaseDate() );
pluginVersion.setMinParentVersion( version.getMinParentVersion() );
pluginVersion.setMaxParentVersion( version.getMaxParentVersion() );
// TODO: use factory for DI?
org.pentaho.marketplace.domain.model.entities.serialization.jaxb.dto.DevelopmentStage dtoDevStage = version.getDevelopmentStage();
if ( dtoDevStage != null ) {
IDevelopmentStage devStage = new DevelopmentStage( dtoDevStage.getLane().value(),
String.valueOf( dtoDevStage.getPhase() ) );
pluginVersion.setDevelopmentStage( devStage );
}
//return the instance
return pluginVersion;
}
private MarketEntryType toEntryType(
org.pentaho.marketplace.domain.model.entities.serialization.jaxb.dto.MarketEntryType entryType ) {
return MarketEntryType.getMarketEntryType( entryType.value() );
}
private ICategory toCategory( org.pentaho.marketplace.domain.model.entities.serialization.jaxb.dto.Category categoryDto ) {
if ( categoryDto == null ) {
return null;
}
ICategory parent = null;
if ( categoryDto.getParent() != null ) {
parent = this.toCategory( categoryDto.getParent() );
}
ICategory category = this.categoryFactory.create( categoryDto.getName(), parent );
return category;
}
// endregion
// endregion
}