{"id":3150,"date":"2012-12-09T22:03:39","date_gmt":"2012-12-09T16:33:39","guid":{"rendered":"http:\/\/JitendraZaa.com\/blog\/?p=3150"},"modified":"2015-07-02T16:30:50","modified_gmt":"2015-07-02T16:30:50","slug":"merge-pdf-in-salesforce-using-java-itextpdf-and-oauth-2","status":"publish","type":"post","link":"https:\/\/www.jitendrazaa.com\/blog\/java\/merge-pdf-in-salesforce-using-java-itextpdf-and-oauth-2\/","title":{"rendered":"Merge PDF in Salesforce Using Java, ITextPDF and OAuth 2"},"content":{"rendered":"<p style=\"text-align: justify;\">Its long time, since i wrote any article because of my busy schedule However this time i came with advance one. In this article we are going to use the J2EE (Servlet) to Merge PDF attachment inside salesforce with the help of <strong>OAuth<\/strong> and <strong>ITextPDF<\/strong> jar file. The reason of writing this article is that there is no native support by Apex to merge two attachments in Salesforce. Either we have to go for AppExchange product like CongaMerg or Drawloop or we can write our own code in any other language like Java and C# and with the help of REST API we can save get and save attachment in Salesforce.<\/p>\n<p style=\"text-align: justify;\">First we will need to setup the OAuth permission so that our local J2EE application can interact with Salesforce. For this login to Salesforce account and Navigate to &#8220;Set up | App Set up | Develop | Remote Access&#8221; and enter the information. Be careful about the &#8220;Callback URL&#8221;. It must match in your code. After creating the &#8220;Remote Access&#8221;, note &#8220;Consumer Key&#8221; and &#8220;Consumer Secret&#8221; which will be needed in your code.<\/p>\n<p style=\"text-align: justify;\">Update : &#8220;Remote Access&#8221; is renamed to Connected App. So throughout this article, if you see image of &#8220;Remote Access&#8221; then please consider it as Connected App.<\/p>\n<figure id=\"attachment_3153\" aria-describedby=\"caption-attachment-3153\" style=\"width: 549px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/jitendrazaa.com\/blog\/wp-content\/uploads\/2012\/12\/Create-Remote-Access-in-Salesforce.com_.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\" wp-image-3153 \" title=\"Create Remote Access in Salesforce.com for OAuth 2\" src=\"https:\/\/i0.wp.com\/jitendrazaa.com\/blog\/wp-content\/uploads\/2012\/12\/Create-Remote-Access-in-Salesforce.com_.png?resize=549%2C378&#038;ssl=1\" alt=\"Create Remote Access in Salesforce.com for OAuth 2\" width=\"549\" height=\"378\" \/><\/a><figcaption id=\"caption-attachment-3153\" class=\"wp-caption-text\">Create Remote Access in Salesforce.com for OAuth 2<\/figcaption><\/figure>\n<p><!--more--><\/p>\n<figure id=\"attachment_3154\" aria-describedby=\"caption-attachment-3154\" style=\"width: 563px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/jitendrazaa.com\/blog\/wp-content\/uploads\/2012\/12\/Remote-Access-Consumer-key-and-Consumer-Secret-Salesforce.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\" wp-image-3154 \" title=\"Get Consumer key and Consumer Secret in Salesforce using Remote Access \" src=\"https:\/\/i0.wp.com\/jitendrazaa.com\/blog\/wp-content\/uploads\/2012\/12\/Remote-Access-Consumer-key-and-Consumer-Secret-Salesforce.png?resize=563%2C344&#038;ssl=1\" alt=\"Get Consumer key and Consumer Secret in Salesforce using Remote Access \" width=\"563\" height=\"344\" \/><\/a><figcaption id=\"caption-attachment-3154\" class=\"wp-caption-text\">Get Consumer key and Consumer Secret in Salesforce using Remote Access<\/figcaption><\/figure>\n<p>So, we will start with setting up the Log4j for our project. Copy below code in &#8220;log4j.properties&#8221; file. you will need\u00a0Apache commons logging jar, which you can find in this article&#8217;s source code.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n# A default log4j configuration for log4j users.\r\n#\r\n# To use this configuration, deploy it into your application's WEB-INF\/classes\r\n# directory.  You are also encouraged to edit it as you like.\r\n\r\n# Configure the console as our one appender\r\nlog4j.appender.A1=org.apache.log4j.ConsoleAppender\r\nlog4j.appender.A1.layout=org.apache.log4j.PatternLayout\r\nlog4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p &#x5B;%c] - %m%n\r\n\r\n# tighten logging on the DataNucleus Categories\r\nlog4j.category.DataNucleus.JDO=WARN, A1\r\nlog4j.category.DataNucleus.Persistence=WARN, A1\r\nlog4j.category.DataNucleus.Cache=WARN, A1\r\nlog4j.category.DataNucleus.MetaData=WARN, A1\r\nlog4j.category.DataNucleus.General=WARN, A1\r\nlog4j.category.DataNucleus.Utility=WARN, A1\r\nlog4j.category.DataNucleus.Transaction=WARN, A1\r\nlog4j.category.DataNucleus.Datastore=WARN, A1\r\nlog4j.category.DataNucleus.ClassLoading=WARN, A1\r\nlog4j.category.DataNucleus.Plugin=WARN, A1\r\nlog4j.category.DataNucleus.ValueGeneration=WARN, A1\r\nlog4j.category.DataNucleus.Enhancer=WARN, A1\r\nlog4j.category.DataNucleus.SchemaTool=WARN, A1\r\n<\/pre>\n<p>Now, create a Servlet with name &#8220;StartServet&#8221; and following code:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.sfdc.pdfmerge.sample;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStreamReader;\r\nimport java.net.URLEncoder;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.logging.Logger;\r\nimport javax.servlet.ServletException;\r\nimport javax.servlet.http.HttpServlet;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport org.apache.http.HttpEntity;\r\nimport org.apache.http.HttpResponse;\r\nimport org.apache.http.NameValuePair;\r\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\r\nimport org.apache.http.client.methods.HttpPost;\r\nimport org.apache.http.impl.client.DefaultHttpClient;\r\nimport org.apache.http.message.BasicNameValuePair;\r\nimport org.json.JSONObject;\r\nimport org.json.JSONTokener;\r\n\r\n\/**\r\n* Servlet implementation class TestOAuth\r\n*\/\r\npublic class StartServet extends HttpServlet {\r\n\tprivate static final long serialVersionUID = 1L;\r\n\t\/**\r\n\t * @see HttpServlet#HttpServlet()\r\n\t *\/\r\n\tpublic StartServet() {\r\n\t\tsuper();\r\n\t\t\/\/ TODO Auto-generated constructor stub\r\n\t}\r\n\r\n\tpublic static final String ACCESS_TOKEN = &quot;ACCESS_TOKEN&quot;;\r\n\tpublic static final String INSTANCE_URL = &quot;INSTANCE_URL&quot;;\r\n\tString oauthURL = &quot;&quot;;\r\n\r\n\tprivate static final Logger log = Logger.getLogger(StartServet.class.getName());\r\n\r\n\tprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\r\n\t\tString sfdcInstance = &quot;https:\/\/ap1.salesforce.com&quot;;\r\n\r\n\t\tString accessToken = (String) request.getSession().getAttribute(ACCESS_TOKEN);\r\n\t\tString code = request.getParameter(&quot;code&quot;);\r\n\r\n\t\tString consumerKey = &quot;3MVG9Y6d_Btp4xp7hZ1hn287sIMQ56.b_dOEBDrKHRbtwByFvFJz0A4ETmj_TbTuu4CsCkknL3ZgoWIzUTBnh&quot;;\r\n\t\tString consumerSecret = &quot;4972968186270851852&quot;;\r\n\t\tString redirectUri = &quot;https:\/\/localhost:8443\/PDFMerge_J2EE\/TestOAuth?isCallBack=1&quot;;\r\n\r\n\t\toauthURL = sfdcInstance + &quot;\/services\/oauth2\/authorize?response_type=code&amp;client_id=&quot; + consumerKey + &quot;&amp;redirect_uri=&quot;\r\n\t\t\t\t+ URLEncoder.encode(redirectUri, &quot;UTF-8&quot;);\r\n\r\n\t\tif (accessToken == null) {\r\n\r\n\t\t\ttry {\r\n\t\t\t\tString isCallBack = request.getParameter(&quot;isCallBack&quot;);\r\n\t\t\t\t\tlog.info(&quot;Starting OAuth&quot;);\r\n\t\t\t\t\tlog.info(&quot;Is CallBack = &quot;+isCallBack);\r\n\r\n\t\t\t\t\t\/**\r\n\t\t\t\t\t * Initiate HTTP request to get the Authorization code\r\n\t\t\t\t\t *\/\r\n\t\t\t\t\tDefaultHttpClient httpclient = new DefaultHttpClient();\r\n\t\t\t\t\tHttpPost httpPost = new HttpPost(oauthURL);\r\n\t\t\t\t\tHttpResponse response1 = httpclient.execute(httpPost);\r\n\r\n\t\t\t\t\t\/**\r\n\t\t\t\t\t * If HTTP status code is 302, means Salesforce trying to redirect to authorize the Local System\r\n\t\t\t\t\t *\/\r\n\t\t\t\t\tif(response1.getStatusLine().getStatusCode() == 302 &amp;&amp; isCallBack == null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\thttpclient.getConnectionManager().shutdown();\r\n\t\t\t\t\t\tlog.info(&quot;Got 302 request from Salesforce so redirect it&quot;);\r\n\t\t\t\t\t\tresponse.sendRedirect(oauthURL);\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlog.info(&quot;Got Callback, Now get the Access Token&quot;);\r\n\r\n\t\t\t\t\t\thttpclient = new DefaultHttpClient();\r\n\t\t\t\t\t\tString tokenUrl = sfdcInstance + &quot;\/services\/oauth2\/token&quot;;\r\n\r\n\t\t\t\t\t\tlog.info(&quot;Code - &quot;+code);\r\n\r\n\t\t\t\t\t\tList qparams = new ArrayList();\r\n\t\t\t\t\t\tqparams.add(new BasicNameValuePair(&quot;code&quot;, code));\r\n\t\t\t\t\t\tqparams.add(new BasicNameValuePair(&quot;grant_type&quot;, &quot;authorization_code&quot;));\r\n\t\t\t\t\t\tqparams.add(new BasicNameValuePair(&quot;client_id&quot;, consumerKey));\r\n\t\t\t\t\t\tqparams.add(new BasicNameValuePair(&quot;client_secret&quot;, consumerSecret));\r\n\t\t\t\t\t\tqparams.add(new BasicNameValuePair(&quot;redirect_uri&quot;, redirectUri));\r\n\r\n\t\t\t\t\t\tHttpPost httpPost2 = new HttpPost(tokenUrl);\r\n\t\t\t\t\t\thttpPost2.setEntity(new UrlEncodedFormEntity(qparams));\r\n\r\n\t\t\t\t\t\t\/**\r\n\t\t\t\t\t\t * Always set this Header to explicitly to get Authrization code else following error will be raised\r\n\t\t\t\t\t\t * unsupported_grant_type\r\n\t\t\t\t\t\t *\/\r\n\t\t\t\t\t\thttpPost2.setHeader(&quot;Content-Type&quot;, &quot;application\/x-www-form-urlencoded&quot;);\r\n\r\n\t\t\t\t\t\tHttpResponse response2 = httpclient.execute(httpPost2);\r\n\r\n\t\t\t\t\t\tHttpEntity entity2 = response2.getEntity();\r\n\t\t\t\t\t\tSystem.out.println(entity2.getContent().toString());\r\n\r\n\t\t\t\t\t\tJSONObject authResponse = new JSONObject(new JSONTokener(new InputStreamReader(entity2.getContent())));\r\n\t\t\t\t\t\tSystem.out.println(authResponse.toString());\r\n\t\t\t\t\t\taccessToken = authResponse.getString(&quot;access_token&quot;);\r\n\t\t\t\t\t\tString instanceUrl = authResponse.getString(&quot;instance_url&quot;);\r\n\r\n\t\t\t\t\t\trequest.getSession().setAttribute(ACCESS_TOKEN, accessToken);\r\n\r\n\t\t\t\t\t\t\/\/ We also get the instance URL from the OAuth response, so set it\r\n\t\t\t\t\t\t\/\/ in the session too\r\n\t\t\t\t\t\trequest.getSession().setAttribute(INSTANCE_URL, instanceUrl);\r\n\r\n\t\t\t\t\t\tlog.info(&quot;Response is : &quot;+authResponse);\r\n\t\t\t\t\t\tlog.info(&quot;Got access token: &quot; + accessToken);\r\n\r\n\t\t\t\t\t\t\/**\r\n\t\t\t\t\t\tIn Below line of code, provide the attachment Ids to merge\r\n\t\t\t\t\t\t*\/\r\n\t\t\t\t\t\tString pdfUrl = &quot;\/PDFMerge_J2EE\/pdfmerge?ids=00P900000038KTm&amp;parentId=00690000007GLNj&amp;mergedDocName=MergedDoc.pdf&quot;;\r\n\t\t\t\t\t\tresponse.sendRedirect(pdfUrl);\r\n\t\t\t\t}\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\t\te.printStackTrace();\r\n\t\t\t} finally {\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\/**\r\n\t * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse\r\n\t * \u00a0 \u00a0 \u00a0response)\r\n\t *\/\r\n\tprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\r\n\t\t\/\/ TODO Auto-generated method stub\r\n\t}\r\n}\r\n<\/pre>\n<p style=\"text-align: justify;\">Above servlet is starting\/landing servlet and authenticate your application with Salesforce using Consumer Key and Consumer Secret. After Authentication using OAuth it will call another servlet to merge PDF and save back in Salesforce.<\/p>\n<p style=\"text-align: justify;\">Now, create a new Servlet which will actually read the attachment Ids from parameter and merge PDF using &#8220;iTextPDF&#8221; jar and save back again in Salesforce.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.sfdc.pdfmerge.sample;\r\n\r\nimport java.io.ByteArrayOutputStream;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.logging.Logger;\r\nimport javax.servlet.http.HttpServlet;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\n@SuppressWarnings(&quot;serial&quot;)\r\npublic class PDFMergeServlet extends HttpServlet {\r\n\tprivate String accessToken;\r\n\tprivate String instanceURL;\r\n\r\n\tprivate static final Logger log = Logger.getLogger(PDFMergeServlet.class.getName());\r\n\r\n\tpublic void doGet(HttpServletRequest req, HttpServletResponse resp)\r\n\t\t\tthrows IOException {\r\n\t    log.severe(&quot;PDF Merge Servlet Called&quot;);\r\n\t\tString sfdcInstance = req.getParameter(&quot;instanceURL&quot;);\r\n\r\n\t\taccessToken = (String)req.getSession().getAttribute(StartServet.ACCESS_TOKEN);\r\n\t\tinstanceURL = (String)req.getSession().getAttribute(StartServet.INSTANCE_URL);\r\n\r\n\t\tif (accessToken == null)\r\n\t\t\treturn;\r\n\r\n\t\tString ids = req.getParameter(&quot;ids&quot;);\r\n\t\tString parentId = req.getParameter(&quot;parentId&quot;);\r\n\t\tString mergedDocName = req.getParameter(&quot;mergedDocName&quot;);\r\n\r\n\t\tList pdfs = new ArrayList();\r\n\t\tByteArrayOutputStream o = null;\r\n\t\tif (accessToken != null &amp;&amp; ids != null &amp;&amp; parentId != null)\r\n\t\t{\r\n\t\t\tSalesforce sfdc = new Salesforce(instanceURL, accessToken);\r\n\t\t\tfor (String id : ids.split(&quot;,&quot;))\r\n\t\t\t{\r\n\t\t\t\tpdfs.add(sfdc.getAttachment(id));\r\n\t\t\t}\r\n\r\n\t\t\to = new ByteArrayOutputStream();\r\n\t\t\tMergePDF.concatPDFs(pdfs, o, false);\r\n\r\n\t\t\tsfdc.saveAttachment(parentId, mergedDocName, o, &quot;application\/pdf&quot;);\r\n\t\t}\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\tif (o != null)\r\n\t\t\t{\r\n\t\t\t\to.flush();\r\n\t\t\t\to.close();\r\n\t\t\t}\r\n\r\n\t\t\tfor (InputStream pdf : pdfs)\r\n\t\t\t{\r\n\t\t\t\tpdf.close();\r\n\t\t\t}\r\n\t\t}\r\n\t\tcatch (Exception e){e.printStackTrace();}\r\n\r\n\t\tresp.setContentType(&quot;text\/plain&quot;);\r\n\t\tresp.getWriter().println(&quot;Documents merged successfully&quot;);\r\n\t}\r\n\r\n}\r\n<\/pre>\n<p>Create a utility Java Class &#8220;MergePDF&#8221; which is used in above servlet:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.sfdc.pdfmerge.sample;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.io.OutputStream;\r\nimport java.util.ArrayList;\r\nimport java.util.Iterator;\r\nimport java.util.List;\r\n\r\nimport com.itextpdf.text.Document;\r\nimport com.itextpdf.text.pdf.BaseFont;\r\nimport com.itextpdf.text.pdf.PdfContentByte;\r\nimport com.itextpdf.text.pdf.PdfImportedPage;\r\nimport com.itextpdf.text.pdf.PdfReader;\r\nimport com.itextpdf.text.pdf.PdfWriter;\r\n\r\npublic class MergePDF {\r\n\r\n    public static void concatPDFs(List&lt;InputStream&gt; streamOfPDFFiles,\r\n            OutputStream outputStream, boolean paginate) {\r\n\r\n        Document document = new Document();\r\n        try {\r\n            List&lt;InputStream&gt; pdfs = streamOfPDFFiles;\r\n            List&lt;PdfReader&gt; readers = new ArrayList&lt;PdfReader&gt;();\r\n            int totalPages = 0;\r\n            Iterator&lt;InputStream&gt; iteratorPDFs = pdfs.iterator();\r\n\r\n            \/\/ Create Readers for the pdfs.\r\n            while (iteratorPDFs.hasNext()) {\r\n                InputStream pdf = iteratorPDFs.next();\r\n                PdfReader pdfReader = new PdfReader(pdf);\r\n                readers.add(pdfReader);\r\n                totalPages += pdfReader.getNumberOfPages();\r\n            }\r\n            \/\/ Create a writer for the outputstream\r\n            PdfWriter writer = PdfWriter.getInstance(document, outputStream);\r\n\r\n            document.open();\r\n            BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA,\r\n                    BaseFont.CP1252, BaseFont.NOT_EMBEDDED);\r\n            PdfContentByte cb = writer.getDirectContent(); \/\/ Holds the PDF\r\n            \/\/ data\r\n\r\n            PdfImportedPage page;\r\n            int currentPageNumber = 0;\r\n            int pageOfCurrentReaderPDF = 0;\r\n            Iterator&lt;PdfReader&gt; iteratorPDFReader = readers.iterator();\r\n\r\n            \/\/ Loop through the PDF files and add to the output.\r\n            while (iteratorPDFReader.hasNext()) {\r\n                PdfReader pdfReader = iteratorPDFReader.next();\r\n\r\n                \/\/ Create a new page in the target for each source page.\r\n                while (pageOfCurrentReaderPDF &lt; pdfReader.getNumberOfPages()) {\r\n                    document.newPage();\r\n                    pageOfCurrentReaderPDF++;\r\n                    currentPageNumber++;\r\n                    page = writer.getImportedPage(pdfReader,\r\n                            pageOfCurrentReaderPDF);\r\n                    cb.addTemplate(page, 0, 0);\r\n\r\n                    \/\/ Code for pagination.\r\n                    if (paginate) {\r\n                        cb.beginText();\r\n                        cb.setFontAndSize(bf, 9);\r\n                        cb.showTextAligned(PdfContentByte.ALIGN_CENTER, &quot;&quot;\r\n                                + currentPageNumber + &quot; of &quot; + totalPages, 520,\r\n                                5, 0);\r\n                        cb.endText();\r\n                    }\r\n                }\r\n                pageOfCurrentReaderPDF = 0;\r\n            }\r\n            outputStream.flush();\r\n            document.close();\r\n            outputStream.close();\r\n        } catch (Exception e) {\r\n            e.printStackTrace();\r\n        } finally {\r\n            if (document.isOpen())\r\n                document.close();\r\n            try {\r\n                if (outputStream != null)\r\n                    outputStream.close();\r\n            } catch (IOException ioe) {\r\n                ioe.printStackTrace();\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p style=\"text-align: justify;\">you will also need to map the Servlet URL in <strong>&#8220;web.xml<\/strong>&#8221; file, which you can find in source code attached.<br \/>\nTo run above application, enter this URL in address bar &#8220;<span style=\"text-decoration: underline;\">https:\/\/localhost:8443\/PDFMerge_J2EE\/<\/span>&#8220;.<\/p>\n<p style=\"text-align: justify;\">After running above code, first time it will ask you to enter the username and password of salesforce account to allow access to your local application and then it will get all the attachments specified in code and after merging it will again attach it to parent id passed in parameter.<\/p>\n<p style=\"text-align: justify;\">jar files needed to run above code:<\/p>\n<ul>\n<li>commons-codec-1.1.jar<\/li>\n<li>commons-codec-1.6.jar<\/li>\n<li>commons-lang3-3.1.jar<\/li>\n<li>commons-lang3-3.1-javadoc.jar<\/li>\n<li>commons-lang3-3.1-sources.jar<\/li>\n<li>commons-lang3-3.1-tests.jar<\/li>\n<li>commons-logging-1.1.1.jar<\/li>\n<li>fluent-hc-4.2.1.jar<\/li>\n<li>httpclient-4.2.1.jar<\/li>\n<li>httpclient-cache-4.2.1.jar<\/li>\n<li>httpcore-4.2.1.jar<\/li>\n<li>httpmime-4.2.1.jar<\/li>\n<li>itextpdf-5.1.0.jar<\/li>\n<\/ul>\n<p>Also, <strong>please note that Salesforce OAuth can be run only on &#8220;https&#8221; protocol<\/strong>. Below articles are also recommended to read :<\/p>\n<p><a title=\"Simple guide to setup SSL in Tomcat\" href=\"https:\/\/jitendrazaa.com\/blog\/java\/simple-guide-to-setup-ssl-in-tomcat\/\">Simple guide to setup SSL in Tomcat<\/a><br \/>\n<a title=\"Digging Deeper into OAuth 2.0 on Force.com\" href=\"http:\/\/wiki.developerforce.com\/page\/Digging_Deeper_into_OAuth_2.0_on_Force.com\">Digging Deeper into OAuth 2.0 on Force.com<\/a><\/p>\n<p><a href=\"https:\/\/jitendrazaa.com\/blog\/wp-content\/uploads\/2012\/12\/Generate-PDF-Using-ITextPDF-on-Salesforce.zip\">Download Complete Source code for &#8211; Merging PDF Using ITextPDF and OAuth2 on Salesforce using Servlet<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Its long time, since i wrote any article because of my busy schedule However this time i came with advance one. In this article we are going to use the J2EE (Servlet) to Merge PDF attachment inside salesforce with the help of OAuth and ITextPDF jar file. The reason of writing this article is that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"jz_research_post":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[20,3,9],"tags":[120,329,260,331],"class_list":["post-3150","post","type-post","status-publish","format-standard","hentry","category-apex","category-java","category-salesforce","tag-integration","tag-java","tag-oauth","tag-salesforce"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":6152,"url":"https:\/\/www.jitendrazaa.com\/blog\/salesforce\/call-salesforce-rest-api-from-apex\/","url_meta":{"origin":3150,"position":0},"title":"Call Salesforce REST API from Apex","author":"Jitendra","date":"February 27, 2014","format":false,"excerpt":"Use Apex to call Salesforce REST API with sample source code","rel":"","context":"In &quot;Salesforce&quot;","block_context":{"text":"Salesforce","link":"https:\/\/www.jitendrazaa.com\/blog\/category\/salesforce\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":4532,"url":"https:\/\/www.jitendrazaa.com\/blog\/salesforce\/salesforce-to-salesforce-integration-using-named-credentials-in-just-5-lines-of-code\/","url_meta":{"origin":3150,"position":1},"title":"Salesforce to Salesforce integration using Named Credentials in 5 lines","author":"Jitendra","date":"May 29, 2015","format":false,"excerpt":"I have already written article to integrate Salesforce with other Salesforce instance around 3 years back. In last 3 years, Salesforce has changed a lot. This time I will integrate Salesforce with other Salesforce only only in 5 lines of code, can you believe it \u00a0:) ? Check my old\u2026","rel":"","context":"In &quot;Salesforce&quot;","block_context":{"text":"Salesforce","link":"https:\/\/www.jitendrazaa.com\/blog\/category\/salesforce\/"},"img":{"alt_text":"Authenticated Named Credential in Salesforce","src":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2015\/05\/Authenticated-Named-Credential-in-Salesforce.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":31310,"url":"https:\/\/www.jitendrazaa.com\/blog\/salesforce\/salesforce-mcp-server-for-claude-code-mcp-clients-setup\/","url_meta":{"origin":3150,"position":2},"title":"Salesforce MCP Server for Claude Code &amp; MCP Clients: Setup Guide 2026","author":"Jitendra Zaa","date":"January 30, 2026","format":false,"excerpt":"Master Salesforce MCP Server setup for Claude Code and other MCP clients. Step-by-step guide covering DX MCP Server, OAuth setup, hosted servers, and mcp-remote configuration.","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/www.jitendrazaa.com\/blog\/category\/ai\/"},"img":{"alt_text":"Salesforce MCP Server for Claude Code & MCP Clients - Complete Developer Guide 2026","src":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2026\/01\/salesforce-mcp-server-claude-code-guide-featured.png?fit=1200%2C675&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2026\/01\/salesforce-mcp-server-claude-code-guide-featured.png?fit=1200%2C675&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2026\/01\/salesforce-mcp-server-claude-code-guide-featured.png?fit=1200%2C675&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2026\/01\/salesforce-mcp-server-claude-code-guide-featured.png?fit=1200%2C675&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2026\/01\/salesforce-mcp-server-claude-code-guide-featured.png?fit=1200%2C675&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":3851,"url":"https:\/\/www.jitendrazaa.com\/blog\/salesforce\/apex\/send-email-with-generated-pdf-as-attachment-from-trigger\/","url_meta":{"origin":3150,"position":3},"title":"Send Email with Generated PDF as attachment from Trigger &#8211; before Winter 16","author":"Jitendra","date":"April 16, 2014","format":false,"excerpt":"There may be scenario in Salesforce that you need to send a Visualforce page rendered as PDF as a part of Email Attachment. This will be very easy if you want to perform this using Controller or Extension class, we just have to call getContentAsPDF() method of PageReference class and\u2026","rel":"","context":"In &quot;Apex&quot;","block_context":{"text":"Apex","link":"https:\/\/www.jitendrazaa.com\/blog\/category\/salesforce\/apex\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":6309,"url":"https:\/\/www.jitendrazaa.com\/blog\/salesforce\/using-jwt-flow-to-authenticate-nodejs-application-with-salesforce\/","url_meta":{"origin":3150,"position":4},"title":"Using JWT Flow to Authenticate Nodejs application with Salesforce","author":"Jitendra","date":"October 8, 2017","format":false,"excerpt":"Video tutorial and Complete source code to use Salesforce JWT OAuth to authenticate Nodejs Application","rel":"","context":"In &quot;Salesforce&quot;","block_context":{"text":"Salesforce","link":"https:\/\/www.jitendrazaa.com\/blog\/category\/salesforce\/"},"img":{"alt_text":"JWT OAuth","src":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2017\/10\/JWT-OAuth.png?fit=862%2C604&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2017\/10\/JWT-OAuth.png?fit=862%2C604&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2017\/10\/JWT-OAuth.png?fit=862%2C604&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2017\/10\/JWT-OAuth.png?fit=862%2C604&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":30645,"url":"https:\/\/www.jitendrazaa.com\/blog\/salesforce\/salesforce-security-ultimate-guide\/","url_meta":{"origin":3150,"position":5},"title":"Salesforce Security Ultimate Guide","author":"Jitendra Zaa","date":"November 16, 2025","format":false,"excerpt":"Complete guide to Salesforce security covering MFA setup, SOQL injection prevention, OAuth security, Shield encryption, and more. Includes step-by-step instructions, code examples, and checklists that even Salesforce admins can follow to protect their org from the 2025 breach wave.","rel":"","context":"In &quot;Salesforce&quot;","block_context":{"text":"Salesforce","link":"https:\/\/www.jitendrazaa.com\/blog\/category\/salesforce\/"},"img":{"alt_text":"Featured image","src":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2025\/12\/Salesforce-Security-Ultimate-Guide-featured.png?fit=1200%2C630&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2025\/12\/Salesforce-Security-Ultimate-Guide-featured.png?fit=1200%2C630&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2025\/12\/Salesforce-Security-Ultimate-Guide-featured.png?fit=1200%2C630&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2025\/12\/Salesforce-Security-Ultimate-Guide-featured.png?fit=1200%2C630&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jitendrazaa.com\/blog\/wp-content\/uploads\/2025\/12\/Salesforce-Security-Ultimate-Guide-featured.png?fit=1200%2C630&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/posts\/3150","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/comments?post=3150"}],"version-history":[{"count":3,"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/posts\/3150\/revisions"}],"predecessor-version":[{"id":4616,"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/posts\/3150\/revisions\/4616"}],"wp:attachment":[{"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/media?parent=3150"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/categories?post=3150"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jitendrazaa.com\/blog\/wp-json\/wp\/v2\/tags?post=3150"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}