├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── msiops │ │ └── premailer │ │ ├── Premailer.java │ │ └── PremailerInterface.java └── ruby │ └── premailer │ └── premailer_contact.rb └── test └── java └── com └── msiops └── premailer ├── PremailerInlineHTMLTest.java └── PremailerPlainTextTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Maven template 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | release.properties 9 | dependency-reduced-pom.xml 10 | buildNumber.properties 11 | .mvn/timing.properties 12 | 13 | 14 | ### Java template 15 | *.class 16 | 17 | # Mobile Tools for Java (J2ME) 18 | .mtj.tmp/ 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.ear 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2015 Matthew de Detrich, mdedetrich@gmail.com 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Premailer Wrapper 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/org.mdedetrich/java-premailer-wrapper?label=java-premailer-wrapper%40maven-central)](https://maven-badges.herokuapp.com/maven-central/org.mdedetrich/java-premailer-wrapper) 4 | 5 | ## What is this? 6 | 7 | This java wrapper around [Premailer](https://github.com/premailer/premailer). It is roughly based on the 8 | wrapper from [here](https://github.com/r-shah/java-premailer-wrapper), with a few important differences 9 | 10 | - It works 11 | - Premailer is brought in as a dependency rather than being directly included in the source. This allows you 12 | to easily bump Premailer as a dependency 13 | - Added a method that allows you to terminate a Premailer instance (`PremailerInterface.destroyInstance`) 14 | - A fix for JDK 1.8 in regards to conflicts for `.merge ` method (see [here](https://github.com/jruby/jruby/issues/1249)) 15 | - The `Premailer` class no longer acts like a pseudo singleton. Its up to the user to manage the instance (typically you 16 | would store this in a Singleton to reuse the `PremailerInstance`) 17 | - Uses a versioning scheme to identify between Premailer releases, i.e. 1.0_1.8.7 means version 1 using Premailer 1.8.7 18 | 19 | ## Dependency Info 20 | 21 | Currently hosted on maven central, with the following details 22 | 23 | ```xml 24 | 25 | org.mdedetrich 26 | java-premailer-wrapper 27 | 1.3_1.8.7 28 | 29 | ``` 30 | 31 | If you haven't already done so, you need to add the `Rubygems` maven repository, i.e. 32 | 33 | ```xml 34 | 35 | rubygems-releases 36 | http://rubygems-proxy.torquebox.org/releases 37 | 38 | ``` 39 | 40 | ## Building 41 | 42 | You can build a jar by doing 43 | 44 | ``` 45 | mvn compile 46 | mvn package 47 | ``` 48 | 49 | ## Usage 50 | 51 | To use, do something like this 52 | 53 | ```java 54 | String testHtml = "

test

"; 55 | 56 | // Create a Premailer 57 | Premailer premailer = new Premailer() 58 | 59 | // Get the instance 60 | PremailerInterface premailerInterface = premailer.getPremailerInstance(); 61 | 62 | // Pass your options in form of HashMap 63 | Map options = new HashMap( ); 64 | 65 | // Pass at least this option for html string 66 | options.put( "with_html_string", true ); 67 | 68 | System.out.print( premailerInterface.plain_text(testHtml, options) ); 69 | System.out.print( premailerInterface.inline_css(testHtml, options) ); 70 | 71 | // Shut it down 72 | premailer.destroyInstance(); 73 | ``` 74 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.mdedetrich 5 | java-premailer-wrapper 6 | jar 7 | 1.3_1.8.7 8 | java-premailer-wrapper 9 | java-premailer-wrapper 10 | https://github.com/mdedetrich/java-premailer-wrapper 11 | 12 | 13 | Apache-2 14 | http://opensource.org/licenses/Apache-2.0 15 | repo 16 | 17 | 18 | 19 | git@github.com:mdedetrich/java-premailer-wrapper.git 20 | scm:git:git@github.com:mdedetrich/java-premailer-wrapper.git 21 | 22 | 23 | 24 | mdedetrich 25 | Matthew de Detrich 26 | mdedetrich@gmail.com 27 | 28 | 29 | 30 | 31 | rubygems-proxy 32 | Rubygems Proxy 33 | http://rubygems-proxy.torquebox.org/releases 34 | default 35 | 36 | true 37 | 38 | 39 | false 40 | never 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-compiler-plugin 49 | 3.3 50 | 51 | 1.8 52 | 1.8 53 | 54 | 55 | 56 | 57 | de.saumya.mojo 58 | gem-maven-plugin 59 | 1.0.10 60 | 61 | true 62 | 63 | 64 | 65 | 66 | initialize 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-gpg-plugin 74 | 1.6 75 | 76 | 77 | sign-artifacts 78 | verify 79 | 80 | sign 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-source-plugin 88 | 3.2.1 89 | 90 | 91 | attach-sources 92 | 93 | jar 94 | 95 | 96 | 97 | 98 | 99 | org.apache.maven.plugins 100 | maven-javadoc-plugin 101 | 3.3.0 102 | 103 | 104 | attach-javadocs 105 | 106 | jar 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | junit 117 | junit 118 | 4.12 119 | test 120 | 121 | 122 | org.databene 123 | contiperf 124 | 2.2.0 125 | test 126 | 127 | 128 | org.jruby 129 | jruby 130 | 9.1.2.0 131 | 132 | 133 | rubygems 134 | premailer 135 | 1.8.7 136 | gem 137 | 138 | 139 | rubygems 140 | nokogiri 141 | 1.6.8 142 | gem 143 | 144 | 145 | rubygems 146 | htmlentities 147 | 4.3.4 148 | gem 149 | 150 | 151 | rubygems 152 | css_parser 153 | 1.4.5 154 | gem 155 | 156 | 157 | rubygems 158 | addressable 159 | 2.8.0 160 | gem 161 | 162 | 163 | rubygems 164 | public_suffix 165 | 4.0.6 166 | gem 167 | 168 | 169 | 170 | 171 | deployment 172 | releases 173 | https://oss.sonatype.org/service/local/staging/deploy/maven2 174 | 175 | 176 | deployment 177 | snapshots 178 | https://oss.sonatype.org/content/repositories/snapshots 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /src/main/java/com/msiops/premailer/Premailer.java: -------------------------------------------------------------------------------- 1 | package com.msiops.premailer; 2 | 3 | import org.jruby.embed.PathType; 4 | import org.jruby.embed.ScriptingContainer; 5 | 6 | /** 7 | * Class that holds the Premailer Interface instance 8 | */ 9 | 10 | public final class Premailer { 11 | 12 | private ScriptingContainer container; 13 | private PremailerInterface instance; 14 | private Boolean customContainer = false; 15 | private Boolean terminated = false; 16 | 17 | private static String path = "premailer/premailer_contact.rb"; 18 | 19 | /** 20 | * Creates a new Premailer. Note that you only need to create this once, its recommended you 21 | * store this in a Singleton so you reuse it for multiple calls 22 | */ 23 | 24 | public Premailer(){ 25 | container = new ScriptingContainer(); 26 | container.setClassLoader(container.getClass().getClassLoader()); 27 | Object receiver = container.runScriptlet(PathType.CLASSPATH, path); 28 | 29 | // Create instance of premailer interface 30 | instance = container.getInstance( receiver, 31 | PremailerInterface.class ); 32 | } 33 | 34 | /** 35 | * Creates a Premailer out of an already existing JRuby Scripting Container. Useful if you already use 36 | * JRuby elsewhere in your application 37 | * @param container The container for Premailer to use 38 | */ 39 | 40 | public Premailer(ScriptingContainer container){ 41 | if (container == null) { 42 | throw new IllegalArgumentException(); 43 | } else { 44 | this.container = container; 45 | customContainer = true; 46 | Object receiver = container.runScriptlet(PathType.CLASSPATH, path); 47 | instance = container.getInstance( receiver, 48 | PremailerInterface.class ); 49 | } 50 | } 51 | 52 | /** 53 | * Retrieves a Premailer instance, use this to interface with Premailer 54 | * @return The Premailer Instance 55 | */ 56 | 57 | public PremailerInterface getPremailerInstance() { 58 | return instance; 59 | } 60 | 61 | /** 62 | * Returns the JRuby Scripting Container, shouldn't really need this 63 | * @return JRuby Scripining Container 64 | */ 65 | 66 | public ScriptingContainer getScriptingContainer() { 67 | return container; 68 | } 69 | 70 | /** 71 | * Whether or not the Premailer was created with a custom Scripting Container 72 | * @return Premailer created with custom scripting container 73 | */ 74 | 75 | public Boolean usesCustomScriptingContainer() { 76 | return customContainer; 77 | } 78 | 79 | /** 80 | * Destroy's a the permailer instance 81 | */ 82 | 83 | public void destroyInstance() { 84 | if (terminated) { 85 | throw new IllegalArgumentException("Premailer already terminated"); 86 | } 87 | 88 | if (instance != null) { 89 | instance = null; 90 | } 91 | 92 | if (container != null) { 93 | container.terminate(); 94 | terminated = true; 95 | } 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/com/msiops/premailer/PremailerInterface.java: -------------------------------------------------------------------------------- 1 | package com.msiops.premailer; 2 | 3 | import java.util.Map; 4 | 5 | public interface PremailerInterface { 6 | 7 | String inline_css( String html, Map options ); 8 | 9 | String plain_text( String html, Map options ); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/ruby/premailer/premailer_contact.rb: -------------------------------------------------------------------------------- 1 | require "java" 2 | require "premailer" 3 | require "jruby/synchronized" 4 | 5 | #$CLASSPATH << 'target/classes'; 6 | java_import "com.msiops.premailer.PremailerInterface" 7 | 8 | # see https://github.com/jruby/jruby/issues/1249 9 | 10 | if ENV_JAVA['java.specification.version'] >= '1.8' 11 | class Java::JavaUtil::HashMap 12 | def merge(other) 13 | dup.merge!(other) 14 | end 15 | end 16 | end 17 | 18 | class PremailerContact 19 | include PremailerInterface 20 | include JRuby::Synchronized 21 | 22 | def initialize 23 | end 24 | 25 | def init(html, options) 26 | options.keys.each do |key| 27 | options[(key.to_sym rescue key) || key] = options.delete(key) 28 | end 29 | 30 | @defaultOption = {:adapter => :nokogiri, :input_encoding => 'UTF-8', } 31 | 32 | return Premailer.new(html, options.merge(@defaultOption)) 33 | end 34 | 35 | 36 | def inline_css(html, options) 37 | return init(html, options).to_inline_css 38 | end 39 | 40 | 41 | def plain_text(html, options) 42 | return init(html, options).to_plain_text 43 | end 44 | 45 | end 46 | 47 | PremailerContact.new 48 | -------------------------------------------------------------------------------- /src/test/java/com/msiops/premailer/PremailerInlineHTMLTest.java: -------------------------------------------------------------------------------- 1 | package com.msiops.premailer; 2 | 3 | import static org.hamcrest.CoreMatchers.*; 4 | import static org.junit.Assert.*; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import org.databene.contiperf.PerfTest; 10 | import org.databene.contiperf.junit.ContiPerfRule; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | 14 | import com.msiops.premailer.Premailer; 15 | import com.msiops.premailer.PremailerInterface; 16 | 17 | public class PremailerInlineHTMLTest { 18 | 19 | @Rule 20 | public final ContiPerfRule i = new ContiPerfRule(); 21 | 22 | final PremailerInterface premailer = new Premailer().getPremailerInstance(); 23 | 24 | @Test 25 | public void shouldInlineCss() throws Exception { 26 | final String html = ""; 27 | 28 | final Map options = new HashMap<>(); 29 | options.put("with_html_string", true); 30 | 31 | final String inline = premailer.inline_css(html, options); 32 | 33 | assertThat(inline, is(notNullValue())); 34 | assertThat(inline, is("")); 35 | } 36 | 37 | @PerfTest(invocations = 1000, threads = 5) 38 | @Test 39 | public void shouldInlineCSSInThreadSafeManner() throws Exception { 40 | final String html = ""; 41 | 42 | final Map options = new HashMap<>(); 43 | options.put("with_html_string", true); 44 | 45 | final String inline = premailer.inline_css(html, options); 46 | 47 | assertThat(inline, is(notNullValue())); 48 | assertThat(inline, is("")); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/msiops/premailer/PremailerPlainTextTest.java: -------------------------------------------------------------------------------- 1 | package com.msiops.premailer; 2 | 3 | import static org.hamcrest.CoreMatchers.*; 4 | import static org.junit.Assert.*; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import org.databene.contiperf.PerfTest; 10 | import org.databene.contiperf.junit.ContiPerfRule; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | 14 | import com.msiops.premailer.Premailer; 15 | import com.msiops.premailer.PremailerInterface; 16 | 17 | public class PremailerPlainTextTest { 18 | 19 | @Rule 20 | public final ContiPerfRule i = new ContiPerfRule(); 21 | 22 | final PremailerInterface premailer = new Premailer().getPremailerInstance(); 23 | 24 | @Test 25 | public void shouldPlainText() throws Exception { 26 | final String html = "Sample Text"; 27 | 28 | final Map options = new HashMap<>(); 29 | options.put("with_html_string", true); 30 | 31 | final String inline = premailer.plain_text(html, options); 32 | 33 | assertThat(inline, is(notNullValue())); 34 | assertThat(inline, is("Sample Text")); 35 | } 36 | 37 | @PerfTest(invocations = 1000, threads = 5) 38 | @Test 39 | public void shouldPlainTextInThreadSafeManner() throws Exception { 40 | final String html = "Sample Text"; 41 | 42 | final Map options = new HashMap<>(); 43 | options.put("with_html_string", true); 44 | 45 | final String inline = premailer.plain_text(html, options); 46 | 47 | assertThat(inline, is(notNullValue())); 48 | assertThat(inline, is("Sample Text")); 49 | } 50 | } 51 | --------------------------------------------------------------------------------