<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://community.appian.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices</link><description /><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><item><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices</link><pubDate>Tue, 23 Apr 2024 13:13:35 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:6c41a8cf-e204-4dbd-9544-8310d986a68d</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices#comments</comments><description>Current Revision posted to Guide by Appian Max Team on 4/23/2024 1:13:35 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;A data source in Java is the means of retrieving a connection to a database. A data source object will be registered with a naming service based on the &lt;a href="https://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface"&gt;Java Naming and Directory Interface&lt;/a&gt; (JNDI) API. Thus, to acquire a connection to the database, an Appian plug-in must obtain a JNDI data source connection.&lt;/p&gt;
&lt;p&gt;This document shows the best practices for obtaining a JNDI data source connection from within a custom Appian plug-in. In all the examples a &lt;a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;javax.naming.Context&lt;/a&gt; must be acquired to retrieve a connection.&lt;/p&gt;
&lt;p&gt;Before getting started, the JNDI name of a&amp;nbsp;&lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;data source&lt;/a&gt; must already be known. The easiest way to retrieve this for an existing data source is through the Data Sources drop down on the &lt;a href="https://docs.appian.com/suite/help/latest/Data_Stores.html"&gt;Data Stores&lt;/a&gt; tab.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Accessing the Appian Primary data source through JNDI it not supported, only accessing business data sources is allowed.&lt;/li&gt;
&lt;li&gt;It is recommended to use &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html"&gt;PreparedStatements&lt;/a&gt; where possible and avoid manually building SQL statements using strings which are susceptible to SQL injection attacks when user inputs are not properly sanitized.&lt;/li&gt;
&lt;li&gt;Resources must be properly managed to avoid starving the connection pool which can negatively affect system performance and cause an application outage.&lt;/li&gt;
&lt;li&gt;As connection pooling is used for JNDI connections, only request one when it is needed and release as soon as possible. Retrieving the connection and keeping it open when it&amp;rsquo;s not needed will tie up a connection unnecessarily and deprive another process of using it.&lt;/li&gt;
&lt;li&gt;Use the JNDI connection pool to retrieve a database connection, don&amp;rsquo;t hardcode credentials or implement connection pooling within a plug-in.&lt;/li&gt;
&lt;li&gt;When executing multiple statements that could leave the database inconsistent if one fails &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html"&gt;transactions&lt;/a&gt; should be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resource-management"&gt;Resource Management&lt;/h2&gt;
&lt;p&gt;Once a connection has been retrieved it must be closed when finished to ensure the pool is not starved of available connections. An application server or JDBC driver may attempt to close dangling connections automatically but that should not be relied upon.&lt;/p&gt;
&lt;p&gt;Java 7 offers a convenient &lt;a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;try-with-resources&lt;/a&gt; syntax to automatically close a resource when the code block completes normally or abruptly by exception. This syntax is cleaner than traditional code that uses a &lt;code&gt;finally&lt;/code&gt; block to close connections.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;try (Connection conn = ds.getConnection()) {
  try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer&amp;quot;)) {
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      String name = rs.getString(&amp;quot;name&amp;quot;);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
In the above example the ResultSet is closed when the PreparedStatement is closed. If the PreparedStatement will be reused then the ResultSet should also use the try-with-resources syntax.
&lt;h2 id="function"&gt;Function&lt;/h2&gt;
The only way to access data sources is by injecting the &lt;a style="background-color:#ffcc00;" href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;&lt;code&gt;javax.naming.Context&lt;/code&gt;&lt;/a&gt; into your plug-in function constructor. Plug-in functions, smart services, and servlets using &lt;code&gt;new InitialContext()&lt;/code&gt; will fail when trying to access a data source configured in the &lt;a style="background-color:#ffcc00;" href="https://docs.appian.com/suite/help/latest/Appian_Administration_Console.html"&gt;Administration Console&lt;/a&gt;.
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian ServiceContext with the &lt;a href="http://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;The read operation in this example is better handled by a &lt;a href="https://docs.appian.com/suite/help/23.3/Query_Database_Smart_Service.html"&gt;Query function&lt;/a&gt; and is only shown for illustration purses.&lt;/li&gt;
&lt;li&gt;Functions should not be used to insert/update data due to potential data integrity issues. See &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; for more details. For inserts/updates use a &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; or &lt;a href="#smart_service"&gt;Smart Service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@Function
public String getCustomerNameById(
  ServiceContext sc,
  Context ctx,
  @Parameter long id
) throws NamingException, SQLException {
  DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
  String name = null;
  try (Connection conn = ds.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
      ps.setLong(1, id);
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        name = rs.getString(&amp;quot;name&amp;quot;);
      }
    }
  }
  return name;
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="smart_service"&gt;Smart Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian &lt;a href="https://docs.appian.com/suite/help/21.1/api/com/appiancorp/suiteapi/process/framework/SmartServiceContext.html"&gt;SmartServiceContext&lt;/a&gt; with the &lt;a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;For reading data use a &lt;a href="#function"&gt;Function&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;This example of calling a stored procedure is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@ConnectivityServices
public class UpdateCustomerName extends AppianSmartService {
  private SmartServiceContext smartServiceCtx; 
  private Context ctx;
  private Long id;
  private String name;
  public UpdateCustomerName(SmartServiceContext smartServiceCtx, Context ctx) {
    this.smartServiceCtx = smartServiceCtx;
    this.ctx = ctx;
  }

  @Override
  public void run() throws SmartServiceException {
    try {
      updateCustomerName();
    } catch (NamingException | SQLException e) {
      throw new SmartServiceException.Builder(UpdateCustomerName.class, e).build();
    }
  }

  public void updateCustomerName() throws NamingException, SQLException {
    DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
    try (Connection conn = ds.getConnection()) {
      try (CallableStatement call = conn.prepareCall(&amp;quot;{ call update_customer_name(?, ?) }&amp;quot;)) {
        call.setLong(1, id);
        call.setString(2, name);
        call.execute();
      }
    }
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;CustomerId&amp;quot;)
  public void setCustomerId(Long val) {
    this.id = val;
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;NewCustomerName&amp;quot;)
  public void setNewCustomerName(String val) {
    this.name = val;
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="servlet"&gt;Servlet&lt;/h2&gt;
&lt;p&gt;This example is a simple servlet that connects to a JNDI data source provided by the application server, executes an SQL query and iterates through the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide point access for a specific database report to an external system that is unable to integrate with the data source directly.&lt;/li&gt;
&lt;li&gt;Provide pull access to specific data in highly secure environments that limit direct access and where data cannot be pushed to an external data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Non Use Cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Appian to provide wholesale access to the underlying data source to an external entity.&lt;/li&gt;
&lt;li&gt;Integration with an unsupported database. This is better handled through custom functions and smart services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This example is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use. Adapt the example as you see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;public class DatabaseServlet extends HttpServlet {
  @Override
  protected void doGet(
    Context ctx,
    HttpServletRequest req,
    HttpServletResponse resp
  ) throws ServletException, IOException {
    PrintWriter pw = new PrintWriter(resp.getOutputStream());
    long id = Long.parseLong(req.getParameter(&amp;quot;id&amp;quot;));
    String name = null;

    try {
      DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
      try (Connection conn = ds.getConnection()) {
        try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
          ps.setLong(1, id);
          ResultSet rs = ps.executeQuery();
          if (rs.next()) {
            name = rs.getString(&amp;quot;name&amp;quot;);
          }
        }
      }
    } catch (Exception e) {
      throw new ServletException(&amp;quot;An error occurred while processing the GET request&amp;quot;, e);
    }

    if (name == null) {
      pw.write(&amp;quot;No customer found.&amp;quot;);
    } else {
      pw.write(&amp;quot;Customer Name: &amp;quot; + name);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: integrations, Architecture&lt;/div&gt;
</description></item><item><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices/revision/5</link><pubDate>Tue, 31 Oct 2023 17:58:58 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:6c41a8cf-e204-4dbd-9544-8310d986a68d</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices#comments</comments><description>Revision 5 posted to Guide by joel.larin on 10/31/2023 5:58:58 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;A data source in Java is the means of retrieving a connection to a database. A data source object will be registered with a naming service based on the &lt;a href="https://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface"&gt;Java Naming and Directory Interface&lt;/a&gt; (JNDI) API. Thus, to acquire a connection to the database, an Appian plug-in must obtain a JNDI data source connection.&lt;/p&gt;
&lt;p&gt;This document shows the best practices for obtaining a JNDI data source connection from within a custom Appian plug-in. In all the examples a &lt;a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;javax.naming.Context&lt;/a&gt; must be acquired to retrieve a connection.&lt;/p&gt;
&lt;p&gt;Before getting started, the JNDI name of a&amp;nbsp;&lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;data source&lt;/a&gt; must already be known. The easiest way to retrieve this for an existing data source is through the Data Sources drop down on the &lt;a href="https://docs.appian.com/suite/help/latest/Data_Stores.html"&gt;Data Stores&lt;/a&gt; tab.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Accessing the Appian Primary data source through JNDI it not supported, only accessing business data sources is allowed.&lt;/li&gt;
&lt;li&gt;It is recommended to use &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html"&gt;PreparedStatements&lt;/a&gt; where possible and avoid manually building SQL statements using strings which are susceptible to SQL injection attacks when user inputs are not properly sanitized.&lt;/li&gt;
&lt;li&gt;Resources must be properly managed to avoid starving the connection pool which can negatively affect system performance and cause an application outage.&lt;/li&gt;
&lt;li&gt;As connection pooling is used for JNDI connections, only request one when it is needed and release as soon as possible. Retrieving the connection and keeping it open when it&amp;rsquo;s not needed will tie up a connection unnecessarily and deprive another process of using it.&lt;/li&gt;
&lt;li&gt;Use the JNDI connection pool to retrieve a database connection, don&amp;rsquo;t hardcode credentials or implement connection pooling within a plug-in.&lt;/li&gt;
&lt;li&gt;When executing multiple statements that could leave the database inconsistent if one fails &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html"&gt;transactions&lt;/a&gt; should be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resource-management"&gt;Resource Management&lt;/h2&gt;
&lt;p&gt;Once a connection has been retrieved it must be closed when finished to ensure the pool is not starved of available connections. An application server or JDBC driver may attempt to close dangling connections automatically but that should not be relied upon.&lt;/p&gt;
&lt;p&gt;Java 7 offers a convenient &lt;a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;try-with-resources&lt;/a&gt; syntax to automatically close a resource when the code block completes normally or abruptly by exception. This syntax is cleaner than traditional code that uses a &lt;code&gt;finally&lt;/code&gt; block to close connections.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;try (Connection conn = ds.getConnection()) {
  try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer&amp;quot;)) {
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      String name = rs.getString(&amp;quot;name&amp;quot;);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
In the above example the ResultSet is closed when the PreparedStatement is closed. If the PreparedStatement will be reused then the ResultSet should also use the try-with-resources syntax.
&lt;h2 id="function"&gt;Function&lt;/h2&gt;
The only way to access data sources is by injecting the &lt;a style="background-color:#ffcc00;" href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;&lt;code&gt;javax.naming.Context&lt;/code&gt;&lt;/a&gt; into your plug-in function constructor. Plug-in functions, smart services, and servlets using &lt;code&gt;new InitialContext()&lt;/code&gt; will fail when trying to access a data source configured in the &lt;a style="background-color:#ffcc00;" href="https://docs.appian.com/suite/help/latest/Appian_Administration_Console.html"&gt;Administration Console&lt;/a&gt;.
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian ServiceContext with the &lt;a href="http://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;The read operation in this example is better handled by a &lt;a href="https://docs.appian.com/suite/help/23.3/Query_Database_Smart_Service.html"&gt;Query function&lt;/a&gt; and is only shown for illustration purses.&lt;/li&gt;
&lt;li&gt;Functions should not be used to insert/update data due to potential data integrity issues. See &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; for more details. For inserts/updates use a &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; or &lt;a href="#smart_service"&gt;Smart Service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@Function
public String getCustomerNameById(
  ServiceContext sc,
  Context ctx,
  @Parameter long id
) throws NamingException, SQLException {
  DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
  String name = null;
  try (Connection conn = ds.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
      ps.setLong(1, id);
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        name = rs.getString(&amp;quot;name&amp;quot;);
      }
    }
  }
  return name;
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="smart_service"&gt;Smart Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian &lt;a href="https://docs.appian.com/suite/help/21.1/api/com/appiancorp/suiteapi/process/framework/SmartServiceContext.html"&gt;SmartServiceContext&lt;/a&gt; with the &lt;a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;For reading data use a &lt;a href="#function"&gt;Function&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;This example of calling a stored procedure is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@ConnectivityServices
public class UpdateCustomerName extends AppianSmartService {
  private SmartServiceContext smartServiceCtx; 
  private Context ctx;
  private Long id;
  private String name;
  public UpdateCustomerName(SmartServiceContext smartServiceCtx, Context ctx) {
    this.smartServiceCtx = smartServiceCtx;
    this.ctx = ctx;
  }

  @Override
  public void run() throws SmartServiceException {
    try {
      updateCustomerName();
    } catch (NamingException | SQLException e) {
      throw new SmartServiceException.Builder(UpdateCustomerName.class, e).build();
    }
  }

  public void updateCustomerName() throws NamingException, SQLException {
    DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
    try (Connection conn = ds.getConnection()) {
      try (CallableStatement call = conn.prepareCall(&amp;quot;{ call update_customer_name(?, ?) }&amp;quot;)) {
        call.setLong(1, id);
        call.setString(2, name);
        call.execute();
      }
    }
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;CustomerId&amp;quot;)
  public void setCustomerId(Long val) {
    this.id = val;
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;NewCustomerName&amp;quot;)
  public void setNewCustomerName(String val) {
    this.name = val;
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="servlet"&gt;Servlet&lt;/h2&gt;
&lt;p&gt;This example is a simple servlet that connects to a JNDI data source provided by the application server, executes an SQL query and iterates through the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide point access for a specific database report to an external system that is unable to integrate with the data source directly.&lt;/li&gt;
&lt;li&gt;Provide pull access to specific data in highly secure environments that limit direct access and where data cannot be pushed to an external data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Non Use Cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Appian to provide wholesale access to the underlying data source to an external entity.&lt;/li&gt;
&lt;li&gt;Integration with an unsupported database. This is better handled through custom functions and smart services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This example is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use. Adapt the example as you see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;public class DatabaseServlet extends HttpServlet {
  @Override
  protected void doGet(
    Context ctx,
    HttpServletRequest req,
    HttpServletResponse resp
  ) throws ServletException, IOException {
    PrintWriter pw = new PrintWriter(resp.getOutputStream());
    long id = Long.parseLong(req.getParameter(&amp;quot;id&amp;quot;));
    String name = null;

    try {
      DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
      try (Connection conn = ds.getConnection()) {
        try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
          ps.setLong(1, id);
          ResultSet rs = ps.executeQuery();
          if (rs.next()) {
            name = rs.getString(&amp;quot;name&amp;quot;);
          }
        }
      }
    } catch (Exception e) {
      throw new ServletException(&amp;quot;An error occurred while processing the GET request&amp;quot;, e);
    }

    if (name == null) {
      pw.write(&amp;quot;No customer found.&amp;quot;);
    } else {
      pw.write(&amp;quot;Customer Name: &amp;quot; + name);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: integrations, Architecture&lt;/div&gt;
</description></item><item><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices/revision/4</link><pubDate>Tue, 31 Oct 2023 17:55:29 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:6c41a8cf-e204-4dbd-9544-8310d986a68d</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices#comments</comments><description>Revision 4 posted to Guide by joel.larin on 10/31/2023 5:55:29 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;A data source in Java is the means of retrieving a connection to a database. A data source object will be registered with a naming service based on the &lt;a href="https://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface"&gt;Java Naming and Directory Interface&lt;/a&gt; (JNDI) API. Thus, to acquire a connection to the database, an Appian plug-in must obtain a JNDI data source connection.&lt;/p&gt;
&lt;p&gt;This document shows the best practices for obtaining a JNDI data source connection from within a custom Appian plug-in. In all the examples a &lt;a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;javax.naming.Context&lt;/a&gt; must be acquired to retrieve a connection.&lt;/p&gt;
&lt;p&gt;Before getting started, the JNDI name of a&amp;nbsp;&lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;data source&lt;/a&gt; must already be known. The easiest way to retrieve this for an existing data source is through the Data Sources drop down on the &lt;a href="https://docs.appian.com/suite/help/latest/Data_Stores.html"&gt;Data Stores&lt;/a&gt; tab.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Accessing the Appian Primary data source through JNDI it not supported, only accessing business data sources is allowed.&lt;/li&gt;
&lt;li&gt;It is recommended to use &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html"&gt;PreparedStatements&lt;/a&gt; where possible and avoid manually building SQL statements using strings which are susceptible to SQL injection attacks when user inputs are not properly sanitized.&lt;/li&gt;
&lt;li&gt;Resources must be properly managed to avoid starving the connection pool which can negatively affect system performance and cause an application outage.&lt;/li&gt;
&lt;li&gt;As connection pooling is used for JNDI connections, only request one when it is needed and release as soon as possible. Retrieving the connection and keeping it open when it&amp;rsquo;s not needed will tie up a connection unnecessarily and deprive another process of using it.&lt;/li&gt;
&lt;li&gt;Use the JNDI connection pool to retrieve a database connection, don&amp;rsquo;t hardcode credentials or implement connection pooling within a plug-in.&lt;/li&gt;
&lt;li&gt;When executing multiple statements that could leave the database inconsistent if one fails &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html"&gt;transactions&lt;/a&gt; should be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resource-management"&gt;Resource Management&lt;/h2&gt;
&lt;p&gt;Once a connection has been retrieved it must be closed when finished to ensure the pool is not starved of available connections. An application server or JDBC driver may attempt to close dangling connections automatically but that should not be relied upon.&lt;/p&gt;
&lt;p&gt;Java 7 offers a convenient &lt;a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;try-with-resources&lt;/a&gt; syntax to automatically close a resource when the code block completes normally or abruptly by exception. This syntax is cleaner than traditional code that uses a &lt;code&gt;finally&lt;/code&gt; block to close connections.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;try (Connection conn = ds.getConnection()) {
  try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer&amp;quot;)) {
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      String name = rs.getString(&amp;quot;name&amp;quot;);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
In the above example the ResultSet is closed when the PreparedStatement is closed. If the PreparedStatement will be reused then the ResultSet should also use the try-with-resources syntax.
&lt;h2 id="function"&gt;Function&lt;/h2&gt;
The only way to access data sources is by injecting the &lt;a style="background-color:#ffcc00;" href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;&lt;code&gt;javax.naming.Context&lt;/code&gt;&lt;/a&gt; into your plug-in function constructor. Plug-in functions, smart services, and servlets using &lt;code&gt;new InitialContext()&lt;/code&gt; will fail when trying to access a data source configured in the &lt;a style="background-color:#ffcc00;" href="https://docs.appian.com/suite/help/latest/Appian_Administration_Console.html"&gt;Administration Console&lt;/a&gt;.
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian ServiceContext with the &lt;a href="http://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;The read operation in this example is better handled by a &lt;a href="https://docs.appian.com/suite/help/23.3/Query_Database_Smart_Service.html"&gt;Query function&lt;/a&gt; and is only shown for illustration purses.&lt;/li&gt;
&lt;li&gt;Functions should not be used to insert/update data due to potential data integrity issues. See &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; for more details. For inserts/updates use a &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; or &lt;a href="#smart_service"&gt;Smart Service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@Function
public String getCustomerNameById(
  ServiceContext sc,
  Context ctx,
  @Parameter long id
) throws NamingException, SQLException {
  DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
  String name = null;
  try (Connection conn = ds.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
      ps.setLong(1, id);
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        name = rs.getString(&amp;quot;name&amp;quot;);
      }
    }
  }
  return name;
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="smart_service"&gt;Smart Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian &lt;a href="/w/the-appian-playbook/api/com/appiancorp/suiteapi/process/framework/SmartServiceContext"&gt;SmartServiceContext&lt;/a&gt; with the &lt;a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;For reading data use a &lt;a href="#function"&gt;Function&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;This example of calling a stored procedure is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@ConnectivityServices
public class UpdateCustomerName extends AppianSmartService {
  private SmartServiceContext smartServiceCtx; 
  private Context ctx;
  private Long id;
  private String name;
  public UpdateCustomerName(SmartServiceContext smartServiceCtx, Context ctx) {
    this.smartServiceCtx = smartServiceCtx;
    this.ctx = ctx;
  }

  @Override
  public void run() throws SmartServiceException {
    try {
      updateCustomerName();
    } catch (NamingException | SQLException e) {
      throw new SmartServiceException.Builder(UpdateCustomerName.class, e).build();
    }
  }

  public void updateCustomerName() throws NamingException, SQLException {
    DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
    try (Connection conn = ds.getConnection()) {
      try (CallableStatement call = conn.prepareCall(&amp;quot;{ call update_customer_name(?, ?) }&amp;quot;)) {
        call.setLong(1, id);
        call.setString(2, name);
        call.execute();
      }
    }
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;CustomerId&amp;quot;)
  public void setCustomerId(Long val) {
    this.id = val;
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;NewCustomerName&amp;quot;)
  public void setNewCustomerName(String val) {
    this.name = val;
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="servlet"&gt;Servlet&lt;/h2&gt;
&lt;p&gt;This example is a simple servlet that connects to a JNDI data source provided by the application server, executes an SQL query and iterates through the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide point access for a specific database report to an external system that is unable to integrate with the data source directly.&lt;/li&gt;
&lt;li&gt;Provide pull access to specific data in highly secure environments that limit direct access and where data cannot be pushed to an external data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Non Use Cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Appian to provide wholesale access to the underlying data source to an external entity.&lt;/li&gt;
&lt;li&gt;Integration with an unsupported database. This is better handled through custom functions and smart services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This example is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use. Adapt the example as you see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;public class DatabaseServlet extends HttpServlet {
  @Override
  protected void doGet(
    Context ctx,
    HttpServletRequest req,
    HttpServletResponse resp
  ) throws ServletException, IOException {
    PrintWriter pw = new PrintWriter(resp.getOutputStream());
    long id = Long.parseLong(req.getParameter(&amp;quot;id&amp;quot;));
    String name = null;

    try {
      DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
      try (Connection conn = ds.getConnection()) {
        try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
          ps.setLong(1, id);
          ResultSet rs = ps.executeQuery();
          if (rs.next()) {
            name = rs.getString(&amp;quot;name&amp;quot;);
          }
        }
      }
    } catch (Exception e) {
      throw new ServletException(&amp;quot;An error occurred while processing the GET request&amp;quot;, e);
    }

    if (name == null) {
      pw.write(&amp;quot;No customer found.&amp;quot;);
    } else {
      pw.write(&amp;quot;Customer Name: &amp;quot; + name);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: integrations, Architecture&lt;/div&gt;
</description></item><item><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices/revision/3</link><pubDate>Tue, 31 Oct 2023 17:54:35 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:6c41a8cf-e204-4dbd-9544-8310d986a68d</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices#comments</comments><description>Revision 3 posted to Guide by joel.larin on 10/31/2023 5:54:35 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;A data source in Java is the means of retrieving a connection to a database. A data source object will be registered with a naming service based on the &lt;a href="https://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface"&gt;Java Naming and Directory Interface&lt;/a&gt; (JNDI) API. Thus, to acquire a connection to the database, an Appian plug-in must obtain a JNDI data source connection.&lt;/p&gt;
&lt;p&gt;This document shows the best practices for obtaining a JNDI data source connection from within a custom Appian plug-in. In all the examples a &lt;a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;javax.naming.Context&lt;/a&gt; must be acquired to retrieve a connection.&lt;/p&gt;
&lt;p&gt;Before getting started, the &lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;J&lt;/a&gt;NDI name of a&amp;nbsp;&lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;data source&lt;/a&gt; must already be known. The easiest way to retrieve this for an existing data source is through the Data Sources drop down on the &lt;a href="https://docs.appian.com/suite/help/latest/Data_Stores.html"&gt;Data Stores&lt;/a&gt; tab.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Accessing the Appian Primary data source through JNDI it not supported, only accessing business data sources is allowed.&lt;/li&gt;
&lt;li&gt;It is recommended to use &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html"&gt;PreparedStatements&lt;/a&gt; where possible and avoid manually building SQL statements using strings which are susceptible to SQL injection attacks when user inputs are not properly sanitized.&lt;/li&gt;
&lt;li&gt;Resources must be properly managed to avoid starving the connection pool which can negatively affect system performance and cause an application outage.&lt;/li&gt;
&lt;li&gt;As connection pooling is used for JNDI connections, only request one when it is needed and release as soon as possible. Retrieving the connection and keeping it open when it&amp;rsquo;s not needed will tie up a connection unnecessarily and deprive another process of using it.&lt;/li&gt;
&lt;li&gt;Use the JNDI connection pool to retrieve a database connection, don&amp;rsquo;t hardcode credentials or implement connection pooling within a plug-in.&lt;/li&gt;
&lt;li&gt;When executing multiple statements that could leave the database inconsistent if one fails &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html"&gt;transactions&lt;/a&gt; should be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resource-management"&gt;Resource Management&lt;/h2&gt;
&lt;p&gt;Once a connection has been retrieved it must be closed when finished to ensure the pool is not starved of available connections. An application server or JDBC driver may attempt to close dangling connections automatically but that should not be relied upon.&lt;/p&gt;
&lt;p&gt;Java 7 offers a convenient &lt;a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;try-with-resources&lt;/a&gt; syntax to automatically close a resource when the code block completes normally or abruptly by exception. This syntax is cleaner than traditional code that uses a &lt;code&gt;finally&lt;/code&gt; block to close connections.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;try (Connection conn = ds.getConnection()) {
  try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer&amp;quot;)) {
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      String name = rs.getString(&amp;quot;name&amp;quot;);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
In the above example the ResultSet is closed when the PreparedStatement is closed. If the PreparedStatement will be reused then the ResultSet should also use the try-with-resources syntax.
&lt;h2 id="function"&gt;Function&lt;/h2&gt;
The only way to access data sources is by injecting the &lt;a style="background-color:#ffcc00;" href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;&lt;code&gt;javax.naming.Context&lt;/code&gt;&lt;/a&gt; into your plug-in function constructor. Plug-in functions, smart services, and servlets using &lt;code&gt;new InitialContext()&lt;/code&gt; will fail when trying to access a data source configured in the &lt;a style="background-color:#ffcc00;" href="https://docs.appian.com/suite/help/latest/Appian_Administration_Console.html"&gt;Administration Console&lt;/a&gt;.
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian ServiceContext with the &lt;a href="http://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;The read operation in this example is better handled by a &lt;a href="https://docs.appian.com/suite/help/23.3/Query_Database_Smart_Service.html"&gt;Query function&lt;/a&gt; and is only shown for illustration purses.&lt;/li&gt;
&lt;li&gt;Functions should not be used to insert/update data due to potential data integrity issues. See &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; for more details. For inserts/updates use a &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; or &lt;a href="#smart_service"&gt;Smart Service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@Function
public String getCustomerNameById(
  ServiceContext sc,
  Context ctx,
  @Parameter long id
) throws NamingException, SQLException {
  DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
  String name = null;
  try (Connection conn = ds.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
      ps.setLong(1, id);
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        name = rs.getString(&amp;quot;name&amp;quot;);
      }
    }
  }
  return name;
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="smart_service"&gt;Smart Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian &lt;a href="/w/the-appian-playbook/api/com/appiancorp/suiteapi/process/framework/SmartServiceContext"&gt;SmartServiceContext&lt;/a&gt; with the &lt;a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;For reading data use a &lt;a href="#function"&gt;Function&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;This example of calling a stored procedure is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@ConnectivityServices
public class UpdateCustomerName extends AppianSmartService {
  private SmartServiceContext smartServiceCtx; 
  private Context ctx;
  private Long id;
  private String name;
  public UpdateCustomerName(SmartServiceContext smartServiceCtx, Context ctx) {
    this.smartServiceCtx = smartServiceCtx;
    this.ctx = ctx;
  }

  @Override
  public void run() throws SmartServiceException {
    try {
      updateCustomerName();
    } catch (NamingException | SQLException e) {
      throw new SmartServiceException.Builder(UpdateCustomerName.class, e).build();
    }
  }

  public void updateCustomerName() throws NamingException, SQLException {
    DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
    try (Connection conn = ds.getConnection()) {
      try (CallableStatement call = conn.prepareCall(&amp;quot;{ call update_customer_name(?, ?) }&amp;quot;)) {
        call.setLong(1, id);
        call.setString(2, name);
        call.execute();
      }
    }
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;CustomerId&amp;quot;)
  public void setCustomerId(Long val) {
    this.id = val;
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;NewCustomerName&amp;quot;)
  public void setNewCustomerName(String val) {
    this.name = val;
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="servlet"&gt;Servlet&lt;/h2&gt;
&lt;p&gt;This example is a simple servlet that connects to a JNDI data source provided by the application server, executes an SQL query and iterates through the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide point access for a specific database report to an external system that is unable to integrate with the data source directly.&lt;/li&gt;
&lt;li&gt;Provide pull access to specific data in highly secure environments that limit direct access and where data cannot be pushed to an external data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Non Use Cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Appian to provide wholesale access to the underlying data source to an external entity.&lt;/li&gt;
&lt;li&gt;Integration with an unsupported database. This is better handled through custom functions and smart services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This example is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use. Adapt the example as you see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;public class DatabaseServlet extends HttpServlet {
  @Override
  protected void doGet(
    Context ctx,
    HttpServletRequest req,
    HttpServletResponse resp
  ) throws ServletException, IOException {
    PrintWriter pw = new PrintWriter(resp.getOutputStream());
    long id = Long.parseLong(req.getParameter(&amp;quot;id&amp;quot;));
    String name = null;

    try {
      DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
      try (Connection conn = ds.getConnection()) {
        try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
          ps.setLong(1, id);
          ResultSet rs = ps.executeQuery();
          if (rs.next()) {
            name = rs.getString(&amp;quot;name&amp;quot;);
          }
        }
      }
    } catch (Exception e) {
      throw new ServletException(&amp;quot;An error occurred while processing the GET request&amp;quot;, e);
    }

    if (name == null) {
      pw.write(&amp;quot;No customer found.&amp;quot;);
    } else {
      pw.write(&amp;quot;Customer Name: &amp;quot; + name);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: integrations, Architecture&lt;/div&gt;
</description></item><item><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices/revision/2</link><pubDate>Tue, 31 Oct 2023 17:52:28 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:6c41a8cf-e204-4dbd-9544-8310d986a68d</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices#comments</comments><description>Revision 2 posted to Guide by joel.larin on 10/31/2023 5:52:28 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;A data source in Java is the means of retrieving a connection to a database. A data source object will be registered with a naming service based on the &lt;a href="https://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface"&gt;Java Naming and Directory Interface&lt;/a&gt; (JNDI) API. Thus, to acquire a connection to the database, an Appian plug-in must obtain a JNDI data source connection.&lt;/p&gt;
&lt;p&gt;This document shows the best practices for obtaining a JNDI data source connection from within a custom Appian plug-in. In all the examples a &lt;a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;javax.naming.Context&lt;/a&gt; must be acquired to retrieve a connection.&lt;/p&gt;
&lt;p&gt;Before getting started, the &lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;J&lt;/a&gt;NDI name of a&amp;nbsp;&lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;data source&lt;/a&gt; must already be known. The easiest way to retrieve this for an existing data source is through the Data Sources drop down on the &lt;a href="https://docs.appian.com/suite/help/latest/Data_Stores.html"&gt;Data Stores&lt;/a&gt; tab.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Accessing the Appian Primary data source through JNDI it not supported, only accessing business data sources is allowed.&lt;/li&gt;
&lt;li&gt;It is recommended to use &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html"&gt;PreparedStatements&lt;/a&gt; where possible and avoid manually building SQL statements using strings which are susceptible to SQL injection attacks when user inputs are not properly sanitized.&lt;/li&gt;
&lt;li&gt;Resources must be properly managed to avoid starving the connection pool which can negatively affect system performance and cause an application outage.&lt;/li&gt;
&lt;li&gt;As connection pooling is used for JNDI connections, only request one when it is needed and release as soon as possible. Retrieving the connection and keeping it open when it&amp;rsquo;s not needed will tie up a connection unnecessarily and deprive another process of using it.&lt;/li&gt;
&lt;li&gt;Use the JNDI connection pool to retrieve a database connection, don&amp;rsquo;t hardcode credentials or implement connection pooling within a plug-in.&lt;/li&gt;
&lt;li&gt;When executing multiple statements that could leave the database inconsistent if one fails &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html"&gt;transactions&lt;/a&gt; should be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resource-management"&gt;Resource Management&lt;/h2&gt;
&lt;p&gt;Once a connection has been retrieved it must be closed when finished to ensure the pool is not starved of available connections. An application server or JDBC driver may attempt to close dangling connections automatically but that should not be relied upon.&lt;/p&gt;
&lt;p&gt;Java 7 offers a convenient &lt;a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;try-with-resources&lt;/a&gt; syntax to automatically close a resource when the code block completes normally or abruptly by exception. This syntax is cleaner than traditional code that uses a &lt;code&gt;finally&lt;/code&gt; block to close connections.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;try (Connection conn = ds.getConnection()) {
  try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer&amp;quot;)) {
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      String name = rs.getString(&amp;quot;name&amp;quot;);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
In the above example the ResultSet is closed when the PreparedStatement is closed. If the PreparedStatement will be reused then the ResultSet should also use the try-with-resources syntax.
&lt;h2 id="function"&gt;Function&lt;/h2&gt;
The only way to access data sources is by injecting the &lt;a style="background-color:#ffcc00;" href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;&lt;code&gt;javax.naming.Context&lt;/code&gt;&lt;/a&gt; into your plug-in function constructor. Plug-in functions, smart services, and servlets using &lt;code&gt;new InitialContext()&lt;/code&gt; will fail when trying to access a data source configured in the &lt;a style="background-color:#ffcc00;" href="https://docs.appian.com/suite/help/latest/Appian_Administration_Console.html"&gt;Administration Console&lt;/a&gt;.
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian ServiceContext with the &lt;a href="http://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;The read operation in this example is better handled by a &lt;a href="https://docs.appian.com/suite/help/latest/Querying_Data_From_an_RDBMS.html"&gt;Query function&lt;/a&gt; and is only shown for illustration purses.&lt;/li&gt;
&lt;li&gt;Functions should not be used to insert/update data due to potential data integrity issues. See &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; for more details. For inserts/updates use a &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; or &lt;a href="#smart_service"&gt;Smart Service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@Function
public String getCustomerNameById(
  ServiceContext sc,
  Context ctx,
  @Parameter long id
) throws NamingException, SQLException {
  DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
  String name = null;
  try (Connection conn = ds.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
      ps.setLong(1, id);
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        name = rs.getString(&amp;quot;name&amp;quot;);
      }
    }
  }
  return name;
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="smart_service"&gt;Smart Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian &lt;a href="/w/the-appian-playbook/api/com/appiancorp/suiteapi/process/framework/SmartServiceContext"&gt;SmartServiceContext&lt;/a&gt; with the &lt;a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;For reading data use a &lt;a href="#function"&gt;Function&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;This example of calling a stored procedure is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@ConnectivityServices
public class UpdateCustomerName extends AppianSmartService {
  private SmartServiceContext smartServiceCtx; 
  private Context ctx;
  private Long id;
  private String name;
  public UpdateCustomerName(SmartServiceContext smartServiceCtx, Context ctx) {
    this.smartServiceCtx = smartServiceCtx;
    this.ctx = ctx;
  }

  @Override
  public void run() throws SmartServiceException {
    try {
      updateCustomerName();
    } catch (NamingException | SQLException e) {
      throw new SmartServiceException.Builder(UpdateCustomerName.class, e).build();
    }
  }

  public void updateCustomerName() throws NamingException, SQLException {
    DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
    try (Connection conn = ds.getConnection()) {
      try (CallableStatement call = conn.prepareCall(&amp;quot;{ call update_customer_name(?, ?) }&amp;quot;)) {
        call.setLong(1, id);
        call.setString(2, name);
        call.execute();
      }
    }
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;CustomerId&amp;quot;)
  public void setCustomerId(Long val) {
    this.id = val;
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;NewCustomerName&amp;quot;)
  public void setNewCustomerName(String val) {
    this.name = val;
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="servlet"&gt;Servlet&lt;/h2&gt;
&lt;p&gt;This example is a simple servlet that connects to a JNDI data source provided by the application server, executes an SQL query and iterates through the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide point access for a specific database report to an external system that is unable to integrate with the data source directly.&lt;/li&gt;
&lt;li&gt;Provide pull access to specific data in highly secure environments that limit direct access and where data cannot be pushed to an external data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Non Use Cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Appian to provide wholesale access to the underlying data source to an external entity.&lt;/li&gt;
&lt;li&gt;Integration with an unsupported database. This is better handled through custom functions and smart services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This example is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use. Adapt the example as you see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;public class DatabaseServlet extends HttpServlet {
  @Override
  protected void doGet(
    Context ctx,
    HttpServletRequest req,
    HttpServletResponse resp
  ) throws ServletException, IOException {
    PrintWriter pw = new PrintWriter(resp.getOutputStream());
    long id = Long.parseLong(req.getParameter(&amp;quot;id&amp;quot;));
    String name = null;

    try {
      DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
      try (Connection conn = ds.getConnection()) {
        try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
          ps.setLong(1, id);
          ResultSet rs = ps.executeQuery();
          if (rs.next()) {
            name = rs.getString(&amp;quot;name&amp;quot;);
          }
        }
      }
    } catch (Exception e) {
      throw new ServletException(&amp;quot;An error occurred while processing the GET request&amp;quot;, e);
    }

    if (name == null) {
      pw.write(&amp;quot;No customer found.&amp;quot;);
    } else {
      pw.write(&amp;quot;Customer Name: &amp;quot; + name);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: integrations, Architecture&lt;/div&gt;
</description></item><item><title>Accessing JNDI Data Sources in Plug-ins Best Practices</title><link>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices/revision/1</link><pubDate>Thu, 31 Aug 2023 15:27:36 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:6c41a8cf-e204-4dbd-9544-8310d986a68d</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3240/accessing-jndi-data-sources-in-plug-ins-best-practices#comments</comments><description>Revision 1 posted to Guide by joel.larin on 8/31/2023 3:27:36 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;A data source in Java is the means of retrieving a connection to a database. A data source object will be registered with a naming service based on the &lt;a href="https://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface"&gt;Java Naming and Directory Interface&lt;/a&gt; (JNDI) API. Thus, to acquire a connection to the database, an Appian plug-in must obtain a JNDI data source connection.&lt;/p&gt;
&lt;p&gt;This document shows the best practices for obtaining a JNDI data source connection from within a custom Appian plug-in. In all the examples a &lt;a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;javax.naming.Context&lt;/a&gt; must be acquired to retrieve a connection.&lt;/p&gt;
&lt;p&gt;Before getting started, the &lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;JNDI name of a &lt;/a&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Configuring_Relational_Databases.html"&gt;data source&lt;/a&gt; must already be known. The easiest way to retrieve this for an existing data source is through the Data Sources drop down on the &lt;a href="https://docs.appian.com/suite/help/latest/Data_Stores.html"&gt;Data Stores&lt;/a&gt; tab.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Accessing the Appian Primary data source through JNDI it not supported, only accessing business data sources is allowed.&lt;/li&gt;
&lt;li&gt;It is recommended to use &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html"&gt;PreparedStatements&lt;/a&gt; where possible and avoid manually building SQL statements using strings which are susceptible to &lt;a href="https://www.google.com/url?q=https%3A%2F%2Fwww.owasp.org%2Findex.php%2FSQL_Injection&amp;amp;sa=D&amp;amp;sntz=1&amp;amp;usg=AFQjCNHnVp-5wcLgl_3tcopPWKmDowOxqg"&gt;SQL injection&lt;/a&gt; attacks when user inputs are not properly sanitized.&lt;/li&gt;
&lt;li&gt;Resources must be properly managed to avoid starving the connection pool which can negatively affect system performance and cause an application outage.&lt;/li&gt;
&lt;li&gt;As connection pooling is used for JNDI connections, only request one when it is needed and release as soon as possible. Retrieving the connection and keeping it open when it&amp;rsquo;s not needed will tie up a connection unnecessarily and deprive another process of using it.&lt;/li&gt;
&lt;li&gt;Use the JNDI connection pool to retrieve a database connection, don&amp;rsquo;t hardcode credentials or implement connection pooling within a plug-in.&lt;/li&gt;
&lt;li&gt;When executing multiple statements that could leave the database inconsistent if one fails &lt;a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html"&gt;transactions&lt;/a&gt; should be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resource-management"&gt;Resource Management&lt;/h2&gt;
&lt;p&gt;Once a connection has been retrieved it must be closed when finished to ensure the pool is not starved of available connections. An application server or JDBC driver may attempt to close dangling connections automatically but that should not be relied upon.&lt;/p&gt;
&lt;p&gt;Java 7 offers a convenient &lt;a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;try-with-resources&lt;/a&gt; syntax to automatically close a resource when the code block completes normally or abruptly by exception. This syntax is cleaner than traditional code that uses a &lt;code&gt;finally&lt;/code&gt; block to close connections.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;try (Connection conn = ds.getConnection()) {
  try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer&amp;quot;)) {
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      String name = rs.getString(&amp;quot;name&amp;quot;);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
In the above example the ResultSet is closed when the PreparedStatement is closed. If the PreparedStatement will be reused then the ResultSet should also use the try-with-resources syntax.
&lt;h2 id="function"&gt;Function&lt;/h2&gt;
The only way to access data sources is by injecting the &lt;a style="background-color:#ffcc00;" href="https://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;&lt;code&gt;javax.naming.Context&lt;/code&gt;&lt;/a&gt; into your plug-in function constructor. Plug-in functions, smart services, and servlets using &lt;code&gt;new InitialContext()&lt;/code&gt; will fail when trying to access a data source configured in the &lt;a style="background-color:#ffcc00;" href="https://docs.appian.com/suite/help/latest/Appian_Administration_Console.html"&gt;Administration Console&lt;/a&gt;.
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian ServiceContext with the &lt;a href="http://docs.oracle.com/javase/8/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;The read operation in this example is better handled by a &lt;a href="https://docs.appian.com/suite/help/latest/Querying_Data_From_an_RDBMS.html"&gt;Query function&lt;/a&gt; and is only shown for illustration purses.&lt;/li&gt;
&lt;li&gt;Functions should not be used to insert/update data due to potential data integrity issues. See &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; for more details. For inserts/updates use a &lt;a href="https://docs.appian.com/suite/help/latest/Custom_Function_Plug-ins.html#writer-functions"&gt;writer function&lt;/a&gt; or &lt;a href="#smart_service"&gt;Smart Service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@Function
public String getCustomerNameById(
  ServiceContext sc,
  Context ctx,
  @Parameter long id
) throws NamingException, SQLException {
  DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
  String name = null;
  try (Connection conn = ds.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
      ps.setLong(1, id);
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        name = rs.getString(&amp;quot;name&amp;quot;);
      }
    }
  }
  return name;
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="smart_service"&gt;Smart Service&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not confuse the Appian &lt;a href="/w/the-appian-playbook/api/com/appiancorp/suiteapi/process/framework/SmartServiceContext"&gt;SmartServiceContext&lt;/a&gt; with the &lt;a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html"&gt;Java Naming Context&lt;/a&gt; used to acquire the JNDI data source connection.&lt;/li&gt;
&lt;li&gt;For reading data use a &lt;a href="#function"&gt;Function&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;This example of calling a stored procedure is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;@ConnectivityServices
public class UpdateCustomerName extends AppianSmartService {
  private SmartServiceContext smartServiceCtx; 
  private Context ctx;
  private Long id;
  private String name;
  public UpdateCustomerName(SmartServiceContext smartServiceCtx, Context ctx) {
    this.smartServiceCtx = smartServiceCtx;
    this.ctx = ctx;
  }

  @Override
  public void run() throws SmartServiceException {
    try {
      updateCustomerName();
    } catch (NamingException | SQLException e) {
      throw new SmartServiceException.Builder(UpdateCustomerName.class, e).build();
    }
  }

  public void updateCustomerName() throws NamingException, SQLException {
    DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
    try (Connection conn = ds.getConnection()) {
      try (CallableStatement call = conn.prepareCall(&amp;quot;{ call update_customer_name(?, ?) }&amp;quot;)) {
        call.setLong(1, id);
        call.setString(2, name);
        call.execute();
      }
    }
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;CustomerId&amp;quot;)
  public void setCustomerId(Long val) {
    this.id = val;
  }

  @Input(required = Required.ALWAYS)
  @Name(&amp;quot;NewCustomerName&amp;quot;)
  public void setNewCustomerName(String val) {
    this.name = val;
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="servlet"&gt;Servlet&lt;/h2&gt;
&lt;p&gt;This example is a simple servlet that connects to a JNDI data source provided by the application server, executes an SQL query and iterates through the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide point access for a specific database report to an external system that is unable to integrate with the data source directly.&lt;/li&gt;
&lt;li&gt;Provide pull access to specific data in highly secure environments that limit direct access and where data cannot be pushed to an external data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Non Use Cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Appian to provide wholesale access to the underlying data source to an external entity.&lt;/li&gt;
&lt;li&gt;Integration with an unsupported database. This is better handled through custom functions and smart services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This example is intentionally simple and does not cover all use cases nor handle every edge/error case in a way that would be satisfactory for production use. Adapt the example as you see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;pre class="ui-code" data-mode="java"&gt;public class DatabaseServlet extends HttpServlet {
  @Override
  protected void doGet(
    Context ctx,
    HttpServletRequest req,
    HttpServletResponse resp
  ) throws ServletException, IOException {
    PrintWriter pw = new PrintWriter(resp.getOutputStream());
    long id = Long.parseLong(req.getParameter(&amp;quot;id&amp;quot;));
    String name = null;

    try {
      DataSource ds = (DataSource) ctx.lookup(&amp;quot;jdbc/AppianBusinessDS&amp;quot;);
      try (Connection conn = ds.getConnection()) {
        try (PreparedStatement ps = conn.prepareStatement(&amp;quot;SELECT name FROM customer WHERE id = ?&amp;quot;)) {
          ps.setLong(1, id);
          ResultSet rs = ps.executeQuery();
          if (rs.next()) {
            name = rs.getString(&amp;quot;name&amp;quot;);
          }
        }
      }
    } catch (Exception e) {
      throw new ServletException(&amp;quot;An error occurred while processing the GET request&amp;quot;, e);
    }

    if (name == null) {
      pw.write(&amp;quot;No customer found.&amp;quot;);
    } else {
      pw.write(&amp;quot;Customer Name: &amp;quot; + name);
    }
  }
}&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item></channel></rss>