| 1,Thrifty Foods,com_thriftyfoods | ||
| 2,Safeway,ca_safeway | ||
| -3,Costco,ca_costco | ||
| -4,Hudson's Bay,com_thebay | ||
| +3,Hudson's Bay,com_thebay | ||
| +4,Costco,ca_costco | ||
| -#!/bin/bash | ||
| - | ||
| -# Installs the Web Harvest Java archive. | ||
| -mvn install:install-file -Dfile=lib/web-harvest/webharvest-2.1.jar -DgroupId=org.webharvest -DartifactId=webharvest -Dversion=2.1 -Dpackaging=jar | ||
| +#!/bin/bash | ||
| +mvn install:install-file -Dfile=webharvest-2.1.jar -DgroupId=net.sourceforge.web-harvest -DartifactId=webharvest -Dversion=2.1 -Dpackaging=jar | ||
| + | ||
| </properties> | ||
| </action> | ||
| + <action> | ||
| + <actionName>run</actionName> | ||
| + <packagings> | ||
| + <packaging>jar</packaging> | ||
| + </packagings> | ||
| + <goals> | ||
| + <goal>process-classes</goal> | ||
| + <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal> | ||
| + </goals> | ||
| + <properties> | ||
| + <exec.args>-classpath %classpath ${packageClassName}</exec.args> | ||
| + <exec.executable>java</exec.executable> | ||
| + </properties> | ||
| + </action> | ||
| + <action> | ||
| + <actionName>debug</actionName> | ||
| + <packagings> | ||
| + <packaging>jar</packaging> | ||
| + </packagings> | ||
| + <goals> | ||
| + <goal>process-classes</goal> | ||
| + <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal> | ||
| + </goals> | ||
| + <properties> | ||
| + <exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ${packageClassName}</exec.args> | ||
| + <exec.executable>java</exec.executable> | ||
| + <jpda.listen>true</jpda.listen> | ||
| + </properties> | ||
| + </action> | ||
| + <action> | ||
| + <actionName>profile</actionName> | ||
| + <packagings> | ||
| + <packaging>jar</packaging> | ||
| + </packagings> | ||
| + <goals> | ||
| + <goal>process-classes</goal> | ||
| + <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal> | ||
| + </goals> | ||
| + <properties> | ||
| + <exec.args>-classpath %classpath ${packageClassName}</exec.args> | ||
| + <exec.executable>java</exec.executable> | ||
| + </properties> | ||
| + </action> | ||
| </actions> |
| </dependency> | ||
| <dependency> | ||
| - <groupId>org.webharvest</groupId> | ||
| - <artifactId>webharvest</artifactId> | ||
| - <version>2.1</version> | ||
| - </dependency> | ||
| - <dependency> | ||
| <groupId>commons-io</groupId> | ||
| <artifactId>commons-io</artifactId> | ||
| <version>2.4</version> | ||
| <type>jar</type> | ||
| + </dependency> | ||
| + <dependency> | ||
| + <groupId>org.pegdown</groupId> | ||
| + <artifactId>pegdown</artifactId> | ||
| + <version>1.6.0</version> | ||
| + </dependency> | ||
| + <dependency> | ||
| + <groupId>org.postgresql</groupId> | ||
| + <artifactId>postgresql</artifactId> | ||
| + <version>9.4.1208</version> | ||
| + </dependency> | ||
| + <dependency> | ||
| + <groupId>org.hibernate</groupId> | ||
| + <artifactId>hibernate-entitymanager</artifactId> | ||
| + <version>4.3.1.Final</version> | ||
| + </dependency> | ||
| + <dependency> | ||
| + <groupId>net.sourceforge.web-harvest</groupId> | ||
| + <artifactId>webharvest</artifactId> | ||
| + <version>2.1</version> | ||
| + </dependency> | ||
| + <dependency> | ||
| + <groupId>unknown.binary</groupId> | ||
| + <artifactId>hibernate-jpamodelgen-4.3.1.Final</artifactId> | ||
| + <version>SNAPSHOT</version> | ||
| + <scope>provided</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </build> | ||
| <name>Sales</name> | ||
| + <repositories> | ||
| + <repository> | ||
| + <id>unknown-jars-temp-repo</id> | ||
| + <name>A temporary repository created by NetBeans for libraries and jars it could not identify. Please replace the dependencies in this repository with correct ones and delete this repository.</name> | ||
| + <url>file:${project.basedir}/lib</url> | ||
| + </repository> | ||
| + </repositories> | ||
| </project> | ||
| } catch( UnsupportedEncodingException e ) { | ||
| throw new MessagingException( | ||
| - "Invalid default encoding: '" + charset + "'", e ); | ||
| + "Encoding not found: '" + charset + "'", e ); | ||
| } | ||
| } |
| package com.whitemagicsoftware.sales; | ||
| +import java.io.Serializable; | ||
| + | ||
| /** | ||
| * Provides fluent interface for constructing objects. | ||
| * | ||
| * @author White Magic Software, Ltd. | ||
| */ | ||
| -public class BusinessEntity { | ||
| +public class BusinessEntity implements Serializable { | ||
| /** |
| import java.util.List; | ||
| import javax.mail.internet.AddressException; | ||
| -import org.apache.log4j.BasicConfigurator; | ||
| import org.webharvest.definition.ScraperConfiguration; | ||
| import org.webharvest.runtime.Scraper; | ||
| private final static String DIRECTORY_PREFIX = "scrape"; | ||
| - private void Main() { | ||
| - } | ||
| + private final static int DEFAULT_STRING_SIZE = 8192; | ||
| - private void run() throws Exception { | ||
| - process( getSubscribers() ); | ||
| + private void Main() { | ||
| } | ||
| - private void process( | ||
| - List<Subscriber> subscribers ) { | ||
| - for( Subscriber subscriber : subscribers ) { | ||
| + /** | ||
| + * Iterates over all the subscribers to notify them of when their | ||
| + * subscriptions are ready. | ||
| + * | ||
| + * @throws Exception | ||
| + */ | ||
| + private void process() throws Exception { | ||
| + for( Subscriber subscriber : getSubscribers() ) { | ||
| try { | ||
| - String message = process( subscriber, getVendors( subscriber ) ); | ||
| - notify( subscriber, message ); | ||
| + notify( subscriber, process( subscriber ) ); | ||
| } catch( Exception e ) { | ||
| notify( e ); | ||
| } | ||
| } | ||
| } | ||
| - private String process( Subscriber subscriber, List<Vendor> vendors ) | ||
| + private String process( Subscriber subscriber ) | ||
| throws Exception { | ||
| - StringBuilder result = new StringBuilder(); | ||
| + StringBuilder result = new StringBuilder( DEFAULT_STRING_SIZE ); | ||
| - for( Vendor vendor : vendors ) { | ||
| - String s = process( subscriber, vendor, getProducts( subscriber, vendor ) ); | ||
| - result.append( s ); | ||
| + for( Vendor vendor : getVendors() ) { | ||
| + result.append( process( subscriber, vendor ) ); | ||
| } | ||
| return result.toString(); | ||
| } | ||
| - private String process( | ||
| - Subscriber subscriber, | ||
| - Vendor vendor, | ||
| - List<Product> products ) throws Exception { | ||
| - StringBuilder result = new StringBuilder(); | ||
| + private String process( Subscriber subscriber, Vendor vendor ) | ||
| + throws Exception { | ||
| + StringBuilder result = new StringBuilder( DEFAULT_STRING_SIZE ); | ||
| // Include the vendor name on the first products loop iteration. | ||
| boolean includeVendorName = true; | ||
| - for( Product product : products ) { | ||
| + for( Product product : getProducts( subscriber, vendor ) ) { | ||
| Scraper scraper = getScraper( vendor ); | ||
| - scraper.addVariableToContext( "includeVendorName", includeVendorName ); | ||
| - scraper.addVariableToContext( "locationCode", subscriber.getLocationCode() ); | ||
| - scraper.addVariableToContext( "vendorName", vendor.getName() ); | ||
| - scraper.addVariableToContext( "productName", product.getName() ); | ||
| - scraper.addVariableToContext( "productPath", product.getUrlPath() ); | ||
| + scraper.addVariableToContext( "vendor_name_include", includeVendorName ); | ||
| + scraper.addVariableToContext( "location_code", subscriber.getLocationCode() ); | ||
| + scraper.addVariableToContext( "product_name", product.getName() ); | ||
| + scraper.addVariableToContext( "product_path", product.getUrlPath() ); | ||
| scraper.execute(); | ||
| - Variable message = scraper.getContext().getVar( "message" ); | ||
| + Variable message = scraper.getContext().getVar( "message_body" ); | ||
| result.append( message.toString() ); | ||
| /** | ||
| * Root directory containing scripts/ and templates/ directories. | ||
| - * | ||
| + * | ||
| * @return A non-null String to the resources' root directory. | ||
| */ | ||
| } | ||
| - private List<Vendor> getVendors( Subscriber subscriber ) { | ||
| - return getVendorService().list( subscriber ); | ||
| + private List<Vendor> getVendors() throws Exception { | ||
| + return getVendorService().list(); | ||
| } | ||
| private List<Product> getProducts( Subscriber subscriber, Vendor vendor ) { | ||
| return getProductService().list( subscriber, vendor ); | ||
| } | ||
| + /** | ||
| + * Run with <code>-Djavax.persistence.jdbc.password=</code> set to the | ||
| + * database password. | ||
| + * | ||
| + * @param args Unused. | ||
| + * | ||
| + * @throws Exception Failed to notify subscribers of product sales. | ||
| + */ | ||
| public static void main( String args[] ) throws Exception { | ||
| - new Main().run(); | ||
| + new Main().process(); | ||
| } | ||
| } | ||
| package com.whitemagicsoftware.sales; | ||
| -import java.util.List; | ||
| +import java.util.Date; | ||
| import javax.mail.Address; | ||
| +import javax.mail.internet.AddressException; | ||
| +import javax.mail.internet.InternetAddress; | ||
| +import javax.persistence.Column; | ||
| +import javax.persistence.Entity; | ||
| +import javax.persistence.GeneratedValue; | ||
| +import javax.persistence.GenerationType; | ||
| +import javax.persistence.Id; | ||
| +import javax.persistence.Table; | ||
| +import javax.persistence.Temporal; | ||
| /** | ||
| * Represents a subscriber to receive notifications of sales. | ||
| * | ||
| * @author White Magic Software, Ltd. | ||
| */ | ||
| -public final class Subscriber extends BusinessEntity { | ||
| +@Entity | ||
| +@Table( name = "subscriber" ) | ||
| +public class Subscriber extends BusinessEntity { | ||
| + | ||
| + private static final long serialVersionUID = 0L; | ||
| + | ||
| + @Id | ||
| + @GeneratedValue( strategy = GenerationType.AUTO ) | ||
| + private int id; | ||
| /* Notifications are sent to this address. */ | ||
| private Address address; | ||
| - | ||
| + | ||
| + /* Required by JPA. */ | ||
| + @Column( name = "email" ) | ||
| + private String email; | ||
| + | ||
| /* Postal code, zip code, etc. */ | ||
| + @Column( name = "location_code" ) | ||
| private String locationCode; | ||
| - private Subscriber() { | ||
| + /* When the subscriber was added. */ | ||
| + @Temporal( javax.persistence.TemporalType.DATE ) | ||
| + private Date added; | ||
| + | ||
| + /* When the subscriber information was updated. */ | ||
| + @Temporal( javax.persistence.TemporalType.DATE ) | ||
| + private Date updated; | ||
| + | ||
| + protected Subscriber() { | ||
| } | ||
| - public Address getAddress() { | ||
| + /** | ||
| + * Returns a unique identifier for this subscriber. | ||
| + * | ||
| + * @return The primary key, or 0 if not initialized. | ||
| + */ | ||
| + private int getId() { | ||
| + return this.id; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the address (usually e-mail) for message delivery to this | ||
| + * subscriber. | ||
| + * | ||
| + * @return Usually an InternetAddress instance, never null. | ||
| + * | ||
| + * @throws AddressException Could not parse the e-mail address. | ||
| + */ | ||
| + protected synchronized Address getAddress() throws AddressException { | ||
| + if( this.address == null ) { | ||
| + this.address = createAddress(); | ||
| + } | ||
| + | ||
| return this.address; | ||
| } | ||
| - protected void setAddress(Address address) { | ||
| + /** | ||
| + * Returns the e-mail address associated with the subscriber. | ||
| + * | ||
| + * @return A non-null, possibly empty, instance. | ||
| + */ | ||
| + private synchronized String getEmail() { | ||
| + return nullSafe(this.email); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Used by the builder to set the address. | ||
| + * | ||
| + * @param address | ||
| + */ | ||
| + protected void setAddress( Address address ) { | ||
| this.address = address; | ||
| } | ||
| - | ||
| - protected void setLocationCode(String locationCode){ | ||
| + | ||
| + protected void setLocationCode( String locationCode ) { | ||
| this.locationCode = locationCode; | ||
| } | ||
| - | ||
| - public String getLocationCode() { | ||
| - return this.locationCode; | ||
| + | ||
| + protected String getLocationCode() { | ||
| + return nullSafe(this.locationCode); | ||
| + } | ||
| + | ||
| + protected Address createAddress() throws AddressException { | ||
| + return new InternetAddress( getEmail() ); | ||
| } | ||
| } | ||
| - public Builder withAddress(Address address) { | ||
| - getObject().setAddress(address); | ||
| + public Builder withAddress( Address address ) { | ||
| + getObject().setAddress( address ); | ||
| return getBuilder(); | ||
| } | ||
| - | ||
| - public Builder withLocationCode(String locationCode) { | ||
| - getObject().setLocationCode(locationCode); | ||
| + | ||
| + public Builder withLocationCode( String locationCode ) { | ||
| + getObject().setLocationCode( locationCode ); | ||
| return getBuilder(); | ||
| } | ||
| -/* | ||
| - * The MIT License | ||
| - * | ||
| - * Copyright 2016 White Magic Software, Ltd.. | ||
| - * | ||
| - * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| - * of this software and associated documentation files (the "Software"), to deal | ||
| - * in the Software without restriction, including without limitation the rights | ||
| - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| - * copies of the Software, and to permit persons to whom the Software is | ||
| - * furnished to do so, subject to the following conditions: | ||
| - * | ||
| - * The above copyright notice and this permission notice shall be included in | ||
| - * all copies or substantial portions of the Software. | ||
| - * | ||
| - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| - * THE SOFTWARE. | ||
| - */ | ||
| -package com.whitemagicsoftware.sales; | ||
| - | ||
| -/** | ||
| - * Associates a product with a subscriber. | ||
| - * | ||
| - * @author White Magic Software, Ltd. | ||
| - */ | ||
| -public final class Subscription { | ||
| - | ||
| - private Subscriber subscriber; | ||
| - private Product product; | ||
| - | ||
| - public Subscription() { | ||
| - | ||
| - } | ||
| -} | ||
| package com.whitemagicsoftware.sales; | ||
| -import java.net.URL; | ||
| - | ||
| /** | ||
| * Represents a company that sells products. | ||
| * | ||
| * @author White Magic Software, Ltd. | ||
| */ | ||
| public final class Vendor extends BusinessEntity { | ||
| - | ||
| - /** | ||
| - * Vendor web site. | ||
| - */ | ||
| - private URL webSite; | ||
| - | ||
| /** | ||
| * The script to run for this Vendor. | ||
| } | ||
| + /** | ||
| + * Script name must have an extension of .xml. | ||
| + * | ||
| + * @param scriptName The name of the script for a particular vendor. | ||
| + * @return this.builder | ||
| + */ | ||
| public Builder withScriptName(String scriptName) { | ||
| - getObject().setScriptName(scriptName + ".xml"); | ||
| + getObject().setScriptName(scriptName); | ||
| return getBuilder(); | ||
| } | ||
| import com.whitemagicsoftware.sales.Subscriber; | ||
| import java.util.List; | ||
| -import javax.mail.Message; | ||
| import javax.mail.internet.AddressException; | ||
| package com.whitemagicsoftware.sales.service; | ||
| -import com.whitemagicsoftware.sales.Subscriber; | ||
| import com.whitemagicsoftware.sales.Vendor; | ||
| import java.util.List; | ||
| /** | ||
| - * Returns a list of all vendors for a given subscriber. | ||
| + * Returns a list of all vendors. | ||
| * | ||
| - * @param subscriber The subscriber that would like to be notified when vendor | ||
| - * products go on sale. | ||
| * @return A non-null list, possibly empty. | ||
| + * @throws java.lang.Exception Could not obtain the vendor list. | ||
| */ | ||
| - public List<Vendor> list(Subscriber subscriber); | ||
| + public List<Vendor> list() throws Exception; | ||
| } | ||
| import javax.mail.internet.InternetAddress; | ||
| import javax.mail.internet.MimeMessage; | ||
| +import org.pegdown.PegDownProcessor; | ||
| /** | ||
| String textBody = markdown; | ||
| String htmlBody = toHtml( markdown ); | ||
| - | ||
| + | ||
| Message message = createMessage( recipient, subject ); | ||
| message.setContent( new TextHtmlMultipart( textBody, htmlBody ) ); | ||
| */ | ||
| private String toHtml( String markdown ) { | ||
| - return markdown; | ||
| + return new PegDownProcessor().markdownToHtml( markdown ); | ||
| } | ||
| import com.whitemagicsoftware.sales.service.Service; | ||
| import java.util.ArrayList; | ||
| +import java.util.HashMap; | ||
| import java.util.List; | ||
| +import java.util.Map; | ||
| +import javax.persistence.EntityManager; | ||
| +import javax.persistence.EntityManagerFactory; | ||
| +import javax.persistence.Persistence; | ||
| +import javax.persistence.TypedQuery; | ||
| +import javax.persistence.criteria.CriteriaBuilder; | ||
| +import javax.persistence.criteria.CriteriaQuery; | ||
| +import javax.persistence.criteria.Root; | ||
| /** | ||
| * Superclass to all services. | ||
| - * | ||
| + * | ||
| * @author White Magic Software, Ltd. | ||
| * @param <T> The type of classes this service returns. | ||
| */ | ||
| public abstract class ServiceImpl<T> implements Service { | ||
| - | ||
| + | ||
| + private final static String PERSISTENCE_PASSWORD_PROPERTY | ||
| + = "javax.persistence.jdbc.password"; | ||
| + | ||
| /** | ||
| * Creates a new list of items. | ||
| - * @return | ||
| + * | ||
| + * @return | ||
| */ | ||
| protected List<T> createList() { | ||
| return new ArrayList<>(); | ||
| + } | ||
| + | ||
| + protected CriteriaBuilder getCriteriaBuilder() { | ||
| + return getEntityManager().getCriteriaBuilder(); | ||
| + } | ||
| + | ||
| + private EntityManager getEntityManager() { | ||
| + return getEntityManagerFactory().createEntityManager(); | ||
| + } | ||
| + | ||
| + private EntityManagerFactory getEntityManagerFactory() { | ||
| + return Persistence.createEntityManagerFactory( getPersistenceUnitName(), | ||
| + getPersistenceProperties() ); | ||
| + } | ||
| + | ||
| + private Map getPersistenceProperties() { | ||
| + Map result = new HashMap(); | ||
| + result.put( PERSISTENCE_PASSWORD_PROPERTY, getPersistencePassword() ); | ||
| + | ||
| + return result; | ||
| + } | ||
| + | ||
| + private String getPersistencePassword() { | ||
| + return nullSafe( System.getProperty( PERSISTENCE_PASSWORD_PROPERTY ) ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the empty string if the given string is null. | ||
| + * | ||
| + * @param s The string to un-nullify. | ||
| + * | ||
| + * @return s or "" if s is null. | ||
| + */ | ||
| + private String nullSafe( String s ) { | ||
| + return s == null ? "" : s; | ||
| + } | ||
| + | ||
| + private String getPersistenceUnitName() { | ||
| + return "SALES"; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the list of all entries in the database for the given class type. | ||
| + * | ||
| + * @param clazz The class that represents an entity that has rows in a table | ||
| + * within a database. | ||
| + * | ||
| + * @return The instances of the class populated with data from the database. | ||
| + */ | ||
| + protected List<T> getResultList( Class<T> clazz ) { | ||
| + CriteriaBuilder cb = getCriteriaBuilder(); | ||
| + CriteriaQuery<T> cq = cb.createQuery( clazz ); | ||
| + Root<T> root = cq.from( clazz ); | ||
| + | ||
| + CriteriaQuery<T> all = cq.select( root ); | ||
| + TypedQuery<T> query = getEntityManager().createQuery( all ); | ||
| + return query.getResultList(); | ||
| } | ||
| } |
| import java.util.List; | ||
| import javax.mail.internet.AddressException; | ||
| -import javax.mail.internet.InternetAddress; | ||
| /** | ||
| * @author White Magic Software, Ltd. | ||
| */ | ||
| public class SubscriberServiceImpl extends ServiceImpl<Subscriber> | ||
| implements SubscriberService { | ||
| @Override | ||
| public List<Subscriber> list() throws AddressException { | ||
| - List<Subscriber> result = createList(); | ||
| + return getResultList(Subscriber.class); | ||
| + /* | ||
| Subscriber subscriber = new Subscriber.Builder() | ||
| - .withAddress(new InternetAddress("pennysavr@gmail.com")) | ||
| + .withAddress( new InternetAddress( "Dave.Jarvis@gmail.com" ) ) | ||
| .build(); | ||
| - result.add(subscriber); | ||
| + result.add( subscriber ); | ||
| return result; | ||
| + */ | ||
| } | ||
| } |
| import com.whitemagicsoftware.sales.Vendor; | ||
| import com.whitemagicsoftware.sales.service.VendorService; | ||
| +import java.io.BufferedReader; | ||
| +import java.io.IOException; | ||
| +import java.io.InputStream; | ||
| +import java.io.InputStreamReader; | ||
| +import java.util.ArrayList; | ||
| import java.util.List; | ||
| /** | ||
| * @author White Magic Software, Ltd. | ||
| */ | ||
| public class VendorServiceImpl extends ServiceImpl<Vendor> | ||
| implements VendorService { | ||
| @Override | ||
| - public List<Vendor> list(Subscriber subscriber) { | ||
| + public List<Vendor> list() throws Exception { | ||
| List<Vendor> result = createList(); | ||
| - Vendor vendor = new Vendor.Builder() | ||
| - .withName("Thrifty Foods") | ||
| - .withScriptName("com_thriftyfoods") | ||
| - .build(); | ||
| - | ||
| - result.add(vendor); | ||
| + for( final String filename : getScriptFilenames() ) { | ||
| + Vendor vendor = new Vendor.Builder() | ||
| + .withScriptName( filename ) | ||
| + .build(); | ||
| + | ||
| + result.add( vendor ); | ||
| + | ||
| + } | ||
| return result; | ||
| + } | ||
| + | ||
| + private List<String> getScriptFilenames() throws IOException { | ||
| + List<String> filenames = new ArrayList<>(); | ||
| + | ||
| + try( | ||
| + InputStream in = getResourceAsStream( "scripts" ); | ||
| + BufferedReader br = new BufferedReader( new InputStreamReader( in ) ) ) { | ||
| + String resource; | ||
| + | ||
| + while( (resource = br.readLine()) != null ) { | ||
| + filenames.add( resource ); | ||
| + } | ||
| + } | ||
| + | ||
| + return filenames; | ||
| + } | ||
| + | ||
| + private InputStream getResourceAsStream( String resource ) { | ||
| + final InputStream in | ||
| + = getContextClassLoader().getResourceAsStream( resource ); | ||
| + | ||
| + return in == null ? getClass().getResourceAsStream( resource ) : in; | ||
| + } | ||
| + | ||
| + private ClassLoader getContextClassLoader() { | ||
| + return Thread.currentThread().getContextClassLoader(); | ||
| } | ||
| } |
| +<?xml version="1.0" encoding="UTF-8"?> | ||
| +<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> | ||
| + <persistence-unit name="SALES" transaction-type="RESOURCE_LOCAL"> | ||
| + <provider>org.hibernate.ejb.HibernatePersistence</provider> | ||
| + <properties> | ||
| + <property name="javax.persistence.jdbc.url" value="jdbc:postgresql:sales"/> | ||
| + <property name="javax.persistence.jdbc.user" value="sales"/> | ||
| + <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/> | ||
| + <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/> | ||
| + <property name="hibernate.hbm2ddl.auto" value="validate"/> | ||
| + | ||
| + <!-- Password is set using a command line property of the same name. --> | ||
| + <!--property name="javax.persistence.jdbc.password" value=""/--> | ||
| + </properties> | ||
| + </persistence-unit> | ||
| +</persistence> | ||
| <?xml version="1.0" encoding="UTF-8" ?> | ||
| <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> | ||
| -<log4j:configuration debug="true" | ||
| - xmlns:log4j='http://jakarta.apache.org/log4j/'> | ||
| +<log4j:configuration debug="false" | ||
| + xmlns:log4j='http://jakarta.apache.org/log4j/'> | ||
| <appender name="console" class="org.apache.log4j.ConsoleAppender"> | ||
| - <layout class="org.apache.log4j.PatternLayout"> | ||
| - <param name="ConversionPattern" | ||
| - value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" /> | ||
| - </layout> | ||
| + <layout class="org.apache.log4j.PatternLayout"> | ||
| + <param name="ConversionPattern" | ||
| + value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" /> | ||
| + </layout> | ||
| </appender> | ||
| <root> | ||
| <level value="WARN" /> | ||
| <appender-ref ref="console" /> | ||
| </root> | ||
| </log4j:configuration> | ||
| - | ||
| +<?xml version="1.0" encoding="UTF-8"?> | ||
| +<config charset="UTF-8"> | ||
| + <var-def name="vendor_url">https://www.thriftyfoods.com/product/</var-def> | ||
| + <var-def name="vendor_name">Thrifty Foods</var-def> | ||
| + | ||
| + <var-def name="product_page"> | ||
| + <html-to-xml outputtype="pretty" prunetags="script"> | ||
| + <http url="${vendor_url}${product_path}" /> | ||
| + </html-to-xml> | ||
| + </var-def> | ||
| + | ||
| + <var-def name="product_price"> | ||
| + <xpath expression="(//span[@class='price' and @itemprop='price'])[last()]/text()"> | ||
| + <var name="product_page" /> | ||
| + </xpath> | ||
| + </var-def> | ||
| + | ||
| + <var-def name="product_sale"> | ||
| + <xpath expression="boolean(//div[contains(@class,'on-sale') and @itemprop='offerDetails'])"> | ||
| + <var name="product_page" /> | ||
| + </xpath> | ||
| + </var-def> | ||
| + | ||
| + <var-def name="message_body"> | ||
| + <case> | ||
| + <if condition="${vendor_name_include}"> | ||
| + <template> | ||
| + <file path="templates/header-vendor-name.md"/> | ||
| + </template> | ||
| + </if> | ||
| + </case> | ||
| + | ||
| + <case> | ||
| + <if condition="${product_sale}"> | ||
| + <template> | ||
| + <file path="templates/header-product-name.md"/> | ||
| + <file path="templates/product-price.md"/> | ||
| + </template> | ||
| + </if> | ||
| + </case> | ||
| + </var-def> | ||
| +</config> | ||
| -<?xml version="1.0" encoding="UTF-8"?> | ||
| -<config charset="UTF-8"> | ||
| - | ||
| - <var-def name="vendor">https://www.thriftyfoods.com/product/</var-def> | ||
| - | ||
| - <var-def name="page"> | ||
| - <html-to-xml outputtype="pretty" prunetags="script"> | ||
| - <http url="${vendor}${productPath}" /> | ||
| - </html-to-xml> | ||
| - </var-def> | ||
| - | ||
| - <var-def name="price"> | ||
| - <xpath expression="(//span[@class='price' and @itemprop='price'])[last()]/text()"> | ||
| - <var name="page" /> | ||
| - </xpath> | ||
| - </var-def> | ||
| - | ||
| - <var-def name="sale"> | ||
| - <xpath expression="boolean(//div[contains(@class,'on-sale') and @itemprop='offerDetails'])"> | ||
| - <var name="page" /> | ||
| - </xpath> | ||
| - </var-def> | ||
| - | ||
| - <var-def name="message"> | ||
| - <case> | ||
| - <if condition="${includeVendorName}"> | ||
| - <template> | ||
| - <file path="templates/header-vendor-name.md"/> | ||
| - </template> | ||
| - </if> | ||
| - </case> | ||
| - | ||
| - <template> | ||
| - <file path="templates/header-product-name.md"/> | ||
| - <file path="templates/product-price.md"/> | ||
| - </template> | ||
| - </var-def> | ||
| -</config> | ||
| -${productName} | ||
| +${product_name} | ||
| ---------------------------- | ||
| - | ||
| - |
| -${vendorName} | ||
| +${vendor_name} | ||
| ============================ | ||
| -* ${productDescription} | ||
| +* ${product_description} | ||
| -${productName} | ||
| ----------------------------- | ||
| -Sale price is ${productPrice}. | ||
| +Sale price is ${product_price}. | ||
| +CREATE OR REPLACE FUNCTION vendors() | ||
| + RETURNS TABLE(id integer, script text) AS | ||
| +$BODY$ | ||
| +BEGIN | ||
| + DROP TABLE IF EXISTS vendor_scripts; | ||
| + CREATE TEMP TABLE vendor_scripts(id int, script text); | ||
| + COPY vendor_scripts FROM PROGRAM 'find -L /home/sales/scripts -maxdepth 1 -type f -printf "%f\n" | sed s/_/,/' DELIMITER ',' CSV; | ||
| + RETURN QUERY SELECT * FROM vendor_scripts ORDER BY id; | ||
| +END; | ||
| +$BODY$ | ||
| + LANGUAGE plpgsql VOLATILE SECURITY DEFINER | ||
| + COST 100 | ||
| + ROWS 1000; | ||
| +ALTER FUNCTION vendors() | ||
| + OWNER TO postgres; | ||
| + | ||
| +/* | ||
| + * The MIT License | ||
| + * | ||
| + * Copyright 2016 White Magic Software, Ltd.. | ||
| + * | ||
| + * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| + * of this software and associated documentation files (the "Software"), to deal | ||
| + * in the Software without restriction, including without limitation the rights | ||
| + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| + * copies of the Software, and to permit persons to whom the Software is | ||
| + * furnished to do so, subject to the following conditions: | ||
| + * | ||
| + * The above copyright notice and this permission notice shall be included in | ||
| + * all copies or substantial portions of the Software. | ||
| + * | ||
| + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| + * THE SOFTWARE. | ||
| + */ | ||
| +package com.whitemagicsoftware.sales.service.impl; | ||
| + | ||
| +import com.whitemagicsoftware.sales.Subscriber; | ||
| +import com.whitemagicsoftware.sales.service.SubscriberService; | ||
| +import java.util.List; | ||
| +import javax.mail.internet.AddressException; | ||
| +import org.junit.Assert; | ||
| +import org.junit.Test; | ||
| + | ||
| +/** | ||
| + * Tests the Subscriber Service. | ||
| + * | ||
| + * @author White Magic Software, Ltd. | ||
| + */ | ||
| +public class SubscriberServiceImplTest { | ||
| + | ||
| + public SubscriberServiceImplTest() { | ||
| + } | ||
| + | ||
| + @Test | ||
| + public void testSubscriberList() throws AddressException { | ||
| + SubscriberService ss = getSubscriberService(); | ||
| + List<Subscriber> subscribers = ss.list(); | ||
| + | ||
| + Assert.assertTrue( "Could not obtain subscribers from database.", | ||
| + subscribers.size() > 0 ); | ||
| + } | ||
| + | ||
| + private SubscriberService getSubscriberService() { | ||
| + return new SubscriberServiceImpl(); | ||
| + } | ||
| +} | ||
| Author | djarvis <email> |
|---|---|
| Date | 2016-06-19 22:28:21 GMT-0700 |
| Commit | 79c2c12f06a2c204ddadc2e274685bf73543ad70 |
| Parent | 9e9d4ec |
| Delta | 477 lines added, 183 lines removed, 294-line increase |