/* * [FetchElectronicFacts.java] * * Summary: Get basic facts about a new Electronic product from Amazon AWS API. Used to add new products. * * Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2012-11-30 initial version */ package com.mindprod.aws; import com.mindprod.aws.jax.AWSECommerceService; import com.mindprod.aws.jax.AWSECommerceServicePortType; import com.mindprod.aws.jax.AwsHandlerResolver; import com.mindprod.aws.jax.Image; import com.mindprod.aws.jax.Item; import com.mindprod.aws.jax.ItemAttributes; import com.mindprod.aws.jax.ItemLookup; import com.mindprod.aws.jax.ItemLookupRequest; import com.mindprod.aws.jax.Items; import com.mindprod.aws.jax.OperationRequest; import com.mindprod.aws.jax.Price; import com.mindprod.common18.EIO; import com.mindprod.common18.Misc; import com.mindprod.common18.ST; import com.mindprod.entities.EntifyStrings; import com.mindprod.fastcat.FastCat; import com.mindprod.filetransfer.FileTransfer; import com.mindprod.htmlmacros.macro.Global; import com.mindprod.htmlmacros.support.ConfigurationForMindprod; import com.mindprod.hunkio.HunkIO; import com.mindprod.stores.EStore; import javax.xml.namespace.QName; import javax.xml.ws.Holder; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import static java.lang.System.*; /** * Get basic facts about a new Electronic product from Amazon AWS API. Used to add new products. *

* It does not fetch info about whether the products are in stock. * It does not fetch Tiger or Newegg info. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2012-11-30 initial version * @see com.mindprod.aws.AddBindings * @since 2012-03-04 */ public class FetchElectronicFacts { // declarations /** * true if debugging, and want extra output */ private static final boolean DEBUGGING = false; /** * fetch confidential amazon.com-assigned awsAccessKeyId from environment set awsaccesskeyid=xxxxx */ private static final String AWS_ACCESS_KEY_ID = System.getenv( "awsaccesskeyid" ); /** * fetch confidential amazon.com-assigned awsSecretAccessKey assigned by Amazon set awssecretaccesskey=xxxxx */ private static final String AWS_SECRET_ACCESS_KEY = System.getenv( "awssecretaccesskey" ); /** * Namespace for amazon.com in the USA. Would have to be modified for other countries. */ private static final String NAME_SPACE_URI = "http://webservices.amazon.com/AWSECommerceService/2013-08-01"; /** * name of the SOAP service */ private static final String QNAME = "AWSECommerceService"; /** * location of JAX schmo, locally cached. for amazon.com in the USA. Would have to be modified for other countries. * Original came from http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsd */ private static final String WSDL_LOCATION = "file:///E:/com/mindprod/aws/jax/AWSECommerceService.wsdl"; private static final String USAGE = "\nUsage: FetchElectronicFacts" + ".jar ASIN [-replace]"; /** * where Book cover images files are kept */ private static File electronicCoverDir; /** * where Book files are kept */ private static File electronicDir; /** * Which store do we want to probe for new products. */ private static EStore primaryEStore; /** * fetch public amazon.com-assigned associateTag from environment set awsassociatetag=xxxxx */ private static String associateTag; /** * asin */ private static String asin; /** * price * in form "$69.99" */ private static String price; /** * title of book */ private static String title; // /declarations // methods /** * generate the xxxx.html file containing the Electronics macro with info we gleaned from Amazon. * very stripped down. * * @param target file where write generated Book macro * * @throws java.io.IOException if trouble writing file * @see com.mindprod.stores.TidyElectronicMacros#buildTidiedElectronicsFile */ private static void generate( final File target ) throws IOException { // does not handle birth/death final FastCat sb = new FastCat( 19 ); title = EntifyStrings.entifyHTML( title ); sb.append( "\n" ); sb.append( "\n" ); sb.append( "\n" ); final String generated = sb.toString(); HunkIO.writeEntireFile( target, generated, HunkIO.UTF8 ); out.println( EIO.getCanOrAbsPath( target ) + " written." ); } // /method /** * standard boilerplate to connect to amazon.com AWS server with SOAP * * @return port to use to send requests. * @throws java.net.MalformedURLException if some URLs were mis-specified in the following code. */ private static AWSECommerceServicePortType getPort() throws MalformedURLException { // Set the service: AWSECommerceService service = new AWSECommerceService( new URL( WSDL_LOCATION ), new QName( NAME_SPACE_URI, QNAME ) ); // AwsHandlerResolver does the timestamp, signing and Base64 encoding service.setHandlerResolver( new AwsHandlerResolver( AWS_SECRET_ACCESS_KEY ) ); // Set the service port: return primaryEStore.getPort( service ); } // /method /** * Probe the amazon AWS api for isbn13, results in class vars * * @param replace true if should replace existing image. * * @throws java.net.MalformedURLException if generated URL is malformed. */ private static void queryASIN( final boolean replace ) throws MalformedURLException { // for amazon.com in USA // define magic configuring strings final AWSECommerceServicePortType port = getPort(); // new ItemLookup final ItemLookup itemLookup = new ItemLookup(); // new ItemLookupRequest which is part of the ItemLookup final ItemLookupRequest itemLookupRequest = new ItemLookupRequest(); // odd our lookup request to the the Lookup. final List itemLookupRequests = itemLookup.getRequest(); itemLookupRequests.add( itemLookupRequest ); // Set up the values of the ItemLookupRequest itemLookupRequest.setIdType( "ASIN" ); itemLookupRequest.getItemId().add( asin ); // there is no setItemID method // specify info to include in response final List responseGroup = itemLookupRequest.getResponseGroup(); responseGroup.add( "Images" ); responseGroup.add( "ItemAttributes" ); // set up Holder for the response tree final Holder operationRequest = new Holder<>(); final Holder> items = new Holder<>(); final String marketplaceDomain = ""; // not yet supported final String xmlEscaping = "Single"; final String validate = ""; // Probe Amazon Server, WARNING! Amazon changes the parms to this method frequently. // Inserts awsAccessKeyID and associateTag // note order validate, xmlEscaping, different from itemSearch port.itemLookup( marketplaceDomain, AWS_ACCESS_KEY_ID, associateTag, validate, xmlEscaping, itemLookupRequest, itemLookupRequests, operationRequest, items ); // analyse results final List result = items.value; price = ""; title = ""; // size will usually be 1 final int size = result.get( 0 ).getItem().size(); for ( int i = 0; i < size; i++ ) { final Item item = result.get( 0 ).getItem().get( i ); final Image largeImage = item.getLargeImage(); if ( largeImage != null ) { final URL u = new URL( item.getLargeImage().getURL() ); // u will not be null final File targetImage = new File( electronicCoverDir, asin + ".jpg" ); if ( targetImage.exists() ) { if ( replace ) { out.println( "replacing existing image." ); } else { out.println( "leaving existing image as is." ); } } if ( !targetImage.exists() || replace ) { new FileTransfer().download( u, targetImage, false ); out.println( EIO.getCanOrAbsPath( targetImage ) + " downloaded." ); } } final ItemAttributes attributes = item.getItemAttributes(); if ( attributes == null ) { continue; } if ( title.length() == 0 ) { title = attributes.getTitle(); } final Price priceGroup = attributes.getListPrice(); if ( priceGroup == null ) { continue; } if ( price.length() == 0 ) { price = priceGroup.getFormattedPrice(); } } //end loop to process results } // /method /** * takes an asin on the command line. Creates a Electronics macro for it and downloads cover. * * @param args asin to probe. asin then -replace if want image and html replaced * * @throws java.io.IOException if trouble reading/writing files */ public static void main( String[] args ) throws IOException { // we get info on product from Amazon.com. We get prices from one of three sites. // EStore enum inits needs access to the global configuration Global.installConfiguration( new ConfigurationForMindprod() ); final File webrootDir = new File( Global.configuration.getLocalWebrootWithSlashes() ); electronicDir = new File( webrootDir, "electronic" ); electronicCoverDir = new File( webrootDir, "image/electroniccover" ); primaryEStore = EStore.AMAZONCOM; associateTag = primaryEStore.getAccount(); if ( DEBUGGING ) { out.println( "primaryEStore = " + primaryEStore.name() ); out.println( "associateTag = " + associateTag ); out.println( "AWS_ACCESS_KEY_ID = " + AWS_ACCESS_KEY_ID ); out.println( "AWS_SECRET_ACCESS_KEY = " + AWS_SECRET_ACCESS_KEY ); } if ( !( 1 <= args.length && args.length <= 2 ) ) { throw new IllegalArgumentException( USAGE ); } asin = args[ 0 ]; if ( ST.isEmpty( asin ) || asin.length() != 10 ) { throw new IllegalArgumentException( "\nbad asin [" + asin + "]" + USAGE ); } final boolean replace = ( args.length == 2 && args[ 1 ].equals( "-replace" ) ); final File target = new File( electronicDir, asin + ".html" ); queryASIN( replace ); // this should get us a US price for the primary asin if ( ST.isEmpty( price ) ) { // complex lookup, 2 countries price = UpdatePrices.queryAdjustedPrice( asin ); } if ( target.exists() ) { if ( replace ) { out.println( "replacing existing html file " + EIO.getCanOrAbsPath( target ) ); } else { out.println( "leaving existing html as is. " + EIO.getCanOrAbsPath( target ) ); } } if ( !target.exists() || replace ) { generate( target ); } Misc.trackLastThread(); System.exit( 0 ); } // /method }