miércoles, 29 de junio de 2011

File Upload con ASP.NET en Linux y Apache con HtmlInputFile (Html Control) y en IIS y Windows con FileUpload (Web Control).


Una de las operaciones más frecuentes de las aplicaciones web (Web applications) es la carga de archivos (file upload), que es la acción donde el usuario puede seleccionar un archivo de su computadora y mediante el uso de un formulario en una página web puede transferir el archivo desde el sistema de archivos local hacia el sistema de archivos del servidor donde se encuentre la página web.
ASP .NET tiene dos formas de hacer esta funcionalidad, una mediante un control HtmlInputFile la cual se ha soportado desde el Framework 1.1 y la otra mediante un control FileUpload la cual se soporta a partir del Framework 2.0.



A continuación mostramos el listado de un programa en ASP.NET como ejemplo de la implementación de publicación de documentos utilizando el control HtmlInputFile.


Listado 1.0 ASP.NET upload con un control HtmlInputFile



<%@ Page Language="C#" AutoEventWireup="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

protected void Page_Load(object sender, EventArgs e)
{
}
//Version 1.1
protected void btnPublish_click(object sender, EventArgs args) {
try
{
string publishPath = ConfigurationSettings.AppSettings["publishPath"];
if(publishPath == null)
sspublishPath = "/home/martin/public_html/Downloads/";
if ((htmlinputFile.PostedFile != null) && (htmlinputFile.PostedFile.ContentLength > 0))
{
publish(htmlinputFile, publishPath);
lblmsg.ForeColor = System.Drawing.Color.Black;
lblmsg.Text = "Archivo públicado con éxito";
}
else
{
lblmsg.ForeColor = System.Drawing.Color.Red;
lblmsg.Text = "Falta archivo para publicar";
}
}
catch (Exception ex) {
lblmsg.ForeColor = System.Drawing.Color.Red;
lblmsg.Text = ex.Message;
}
}

public void publish(System.Web.UI.HtmlControls.HtmlInputFile inputFile,
string publishPath)
{
System.IO.FileInfo fi = new System.IO.FileInfo(inputFile.PostedFile.FileName);
string theFile = publishPath + fi.Name;
inputFile.PostedFile.SaveAs(theFile);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server" enctype="multipart/form-data">
<div>
<table width="33%">
<tr>
<td>
1-. Seleccione el archivo</td>
</tr>
<tr>
<td>
<input id="htmlinputFile" maxlength="5120" name="htmlinputFile" size="48"
type="file" runat="server" /></td>
</tr>
<tr>
<td>
2-. Pulse el botón publicar</td>
</tr>
<tr>
<td>
<asp:Button ID="btnPublish" runat="server" Text="Publicar"
OnClick="
btnPublish_click"/>
</td>
</tr>
</table>
<br />
<asp:Label ID="lblmsg" runat="server"></asp:Label>
</div>
</form>
</body>
</html>


Como muestra de la implementación ASP.NET con el control FileUpload, presentamos el siguiente listado.


Listado 2.0 ASP.NET Upload con un control FileUpload


<%@ Page Language="C#" AutoEventWireup="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
}
//Version 2.0 or above
public void btnPublish_Click(object sender,EventArgs args) {
try
{
string publishPath = System.Configuration.ConfigurationManager.AppSettings["publishPath"];
if (fupInputFile.HasFile && (fupInputFile.PostedFile.ContentLength > 0))
{
Publish(fupInputFile, publishPath);
lblmsg.ForeColor = System.Drawing.Color.Black;
lblmsg.Text = "Archivo públicado con éxito";
}
else
{
lblmsg.ForeColor = System.Drawing.Color.Red;
lblmsg.Text = "Falta archivo para publicar";
}
}
catch (Exception ex) {
lblmsg.ForeColor = System.Drawing.Color.Red;
lblmsg.Text = ex.Message;
}
}
public void Publish(FileUpload inputFile,
string publishPath) {
System.IO.FileInfo fi = new System.IO.FileInfo(inputFile.PostedFile.FileName);
string theFile = publishPath + fi.Name;
inputFile.PostedFile.SaveAs(theFile);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<table style="width:100%;">
<tr>
<td>
1-. Seleccione el archivo</td>
</tr>
<tr>
<td>
<asp:FileUpload ID="fupInputFile" runat="server" Width="522" />
</td>
</tr>
<tr>
<td>
2-. Pulse el botón publicar</td>
</tr>
<tr>
<td>
<asp:Button ID="btnPublish" runat="server" Text="Publicar"
OnClick="
btnPublish_Click"/>
</td>
</tr>
</table>
<br />
<asp:Label ID="lblmsg" runat="server"></asp:Label>
</div>
</form>
</body>
</html>

Para ambos casos el archivo de configuración web.config es el mismo, lo que cambia en ambos casos es el valor de la llave publishPath dentro de las etiquetas

<appSettings>
, ya que para el listado 1.0 que lo ejecutaremos en Linux utilizando Apache y Mono.
Por lo que el valor se establece en /home/martin/public_html/Downloads como se muestra en el web.config a continuación:



<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="publishPath" value="/home/martin/public_html/Downloads/"/>
</appSettings>
<connectionStrings/>
<system.web>
<compilation debug="true">
</compilation>
<authentication mode="Windows"/>
</system.web>
</configuration>

En el caso del listado 2.0 lo ejecutaremos en IIS 7.0 y Windows. Por lo que el valor se establece como se muestra en el web.config a continuación:


<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="publishPath" value="C:\inetpub\Downloads\"/>
</appSettings>
<connectionStrings/>
<system.web>
<compilation debug="true">
</compilation>
<authentication mode="Windows"/>
</system.web>
</configuration>

Para asignar el valor de la variable publishPath en el listado 1.0 con la siguiente línea:


string publishPath = ConfigurationSettings.AppSettings["publishPath"];

y para el listado 2.0 se asigna con la siguiente línea:


string publishPath = System.Configuration.ConfigurationManager.AppSettings["publishPath"];

Antes de ejecutar la página en Linux debemos habilitar con los permisos de escritura al directorio donde se guardaran los archivos, para hacerlo en Linux siga los siguientes pasos:


  1. El directorio donde se subiran los archivos debe tener los permisos de escritura para el grupo www.

  2. Si estamos como usuarios normales, cambiamos a una cuenta con privilegios de superusuario y establecemos y establecemos el grupo del directorio (en este ejemplo /home/martin/public_html/Downloads) como www con el siguiente comando:
    # chgrp www Downloads

  3. Ponemos permisos de escritura para el grupo con el siguiente comando:
    # chmod 775 Downloads




Estos comandos se muestran a continuación en la siguiente imagen.




Ahora configuramos la Web Application dentro del archivo httpd.conf del servidor Apache como se muestra en la siguiente imagen:



Para consultar como crear una aplicación Web en Apache revisar el siguiente documento:

Igualmente antes de ejecutar la página en Windows debemos de habilitar con los permisos necesarios el directorio del servidor donde vamos a guardar los documentos, en este ejemplo la ruta configurada como directorio de publicación es C:\inetpub\wwwroot\dowloads, por lo que debemos seguir los siguientes pasos:


  1. Hacer click derecho sobre el directorio, en la ventana Properties seleccionar al usuario IIS_IUSRS (el cuál es el usuario anónimo de IIS), presionar el botón “Edit” para abrir la ventana Permissions.


  2. En la ventana de Permissions en la lista inferior Permissions for IIS_IUSRS seleccionar la casilla con el permiso Write




Creamos la Web Application de forma que se muestre en IIS 7.0 como en la siguiente imagen:

Al ejecutar la página ASP.NET desde FireFox en Linux se mostrará como en las siguientes imágenes:



Al ejecutar la página ASP.NET desde Internet Explorer en Windows se verá como en las siguientes imágenes:



Un error frecuente tanto para la implementación con HtmlInputFile y FileUpload, es tratar de subir un archivo que tenga un tamaño mayor al máximo permitido por la petición al servidor Web (Request)
Para solucionar este error solo hay que agregar la siguiente línea al archivo de configuración, donde definimos el tamaño máximo en Kb del archivo que necesitamos publicar. Por ejemplo en el siguiente web.config se definió un tamaño máximo aproximado a 5 mbs (5120 kb).


<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="publishPath" value="C:\inetpub\Downloads\"/>
</appSettings>
<connectionStrings/>
<system.web>
<compilation debug="true">
</compilation>
<authentication mode="Windows"/>
<httpRuntime maxRequestLength="5120"/>
</system.web>
</configuration>

Para más información sobre este error consultar este enlace: HttpException Maximum Request Length.


sábado, 25 de junio de 2011

Utilizando Windows Management Instrumentation (WMI) con Microsoft .NET

Para las aplicaciones que necesiten implementar una solución de un inventario total o parcial del hardware de la computadora, existe una interfaz de programación llamada WMI (Windows Management Instrumentation) a la cual puede accederse mediante lenguajes scripting como VBScript, Perl,Power Shell o cualquier lenguaje diseñado para el .NET Framework que haga uso de los ensamblados System.Management y System.Management.Instrumentation respectivamente.

Windows Management Instrumentation (WMI) es un conjunto de extensiones del Modelo de drivers de Windows WDM (Windows Driver Model) que es un modelo uniforme de clases que representan los valores del hardware y sistema operativo de la computadora,WMI es además la implementación Microsoft de Web-Based Enterprise Management (WBEM) un estándar de la industria que agrupa tecnologías desarrolladas para unificar la administración de los ambientes de computo, por lo que WMI es compatible con WBEM y provee el soporte para el Common Information Model (CIM) que es el modelo que describe los objetos de administración.

Un inventario de hardware con WMI se realiza mediante búsquedas en el repositorio o base de datos de WMI utilizando un lenguaje de consultas conocido como WQL (WMI Query Language) para encontrar los valores de ciertas propiedades del Hardware. WQL es un subconjunto de SQL.

WMI trabaja dentro del contexto de un ensamblado, siendo el predeterminado root\cli que controla las propiedades y métodos que están disponibles en WMI, la seguridad para el WMI se configura a través del control WMI (wmimgmt.msc), de forma predeterminada los usuarios tienen permisos para consultar el proveedor WMI en una computadora local mediante el comando WMIC, desde la línea de comandos del command prompt.

A continuación las clases de un proyecto de consola que imprime información del hardware del equipo.

1-.Una clase PC que será el objeto de transporte

2-.Una clase PCManager que obtendrá algunos de los valores del Hardware para este ejemplo, mediante consultas WQL del repositorio WMI similar a las consultas SQL para en base de datos.

3-.Finalmente el programa principal que despliega información del equipo.

El resultado de la ejecución del programa se muestra en la siguiente imagen:

Los puntos claves del programa utilizados por la clase PCManager son

  1. Utilizar los ensamblados System.Management y System.Management.Instrumentation.
  2. Crear una instancia de la clase ManagementObjectSearcher
  3. Encapsular cada consulta dentro de la clase ObjectQuery
  4. Obtener los resultados de la búsqueda en una colección de ManagementObject representada por la clase ManagementObjectCollection, para iterar dentro de la colección e imprimir los valores.


También se puede mostrar información desde una página ASP.NET como en el siguiente listado:

El resultado de la ejecucción de la página:

martes, 31 de mayo de 2011

Entendiendo Logging en Aplicaciones para .NET Framework

El logging es un proceso recomendado durante la etapa de pruebas e indispensable durante la etapa de liberación o puesta en producción de aplicaciones de software, logging se refiere a la utilización de una bitácora, registro o log, donde guardemos la información de los diferentes eventos que genero la aplicación entre el tiempo de arranque y durante el tiempo de ejecución.
El registro o log debe proporcionarnos toda información necesaria para realizar con la aplicación las siguientes actividades:

  1. Mantener el rastro de todos sus estados (recording)
  2. Depurar (debugging)
  3. Auditar (audit)
  4. Diagnosticar su estado actual (tracing)

Un factor importante a considerar durante el logging es el performance de la aplicación ya que es un hecho que se incrementará el tiempo de las operaciones de escritura, sobre todo si el logging es requerido para una auditoría, este incremento en el performance se explica de la siguiente manera:

Si una operación del proceso de negocio emplea 5 segundos para ejecutarse y producir un resultado, con el logging necesitará 2 o 3 segundos adicionales para registrarse en el log, por lo que ahora el tiempo es de 7 u 8 segundos para el total de la operación.


El siguiente fragmento de código ilustra este factor:

try
{
//operación de negocio 5 segs.
var output = BussinessObject.ExecuteOperation();
//registro en el log 3 segs.
Logger.WriteLine(output);
//El tiempo total de la transacción es de 8 segs.

}catch(Exception e){

//No está incluído dentro de la transacción.
Logger.WriteLine(e.Message);

}

Hay muchas formas de implementar logging en las aplicaciones, esto varía dependiendo del tipo de aplicación y del requerimiento a satisfacer la forma más sencilla es mediante el uso de las clases contenidas en el ensamblado System.Diagnostics. específicamente con las implementaciones derivadas de la clase TraceListener las cuales son responsables de desplegar o de guardar los mensajes generados por las clases Trace y Debug, hay tres implementaciones de esta clase:

  1. TextWriterTraceListener escribe mensajes a cualquier clase que se derive de Stream
  2. EventLogTraceListener escribe los mensajes a el Event Viewer de Windows.
  3. DefaultTraceListener escribe los mensajes a la ventana de salida (Output window).

El siguiente listado muestra el uso de TextWriterTraceListener y EventLogTraceListener, la lógica del programa es simple, si el programa tiene permisos administrativos usará el event viewer como log, de lo contrario usará un archivo de texto, realizando un intercambio entre los Tracelisteners: EventLogTraceListener y TextWriterTraceListener respectivamente.

Al compilar el listado se debe de incluir la opción /d:TRACE como se muestra en la siguiente imagen:

Al ejecutar el programa mostrará un resultado como en la siguiente imagen:

Con el siguiente código el programa intercambia entre Listeners dependiendo si pertenece al grupo con permisos de administración o no.

Si no pertenece al grupo de administradores utiliza como log un archivo de texto log.txt caso contrario utiliza el Event Viewer (visor de eventos de Windows) como se muestra en la siguiente imagen:


Lo más recomendable para el logging es la utilización de componentes como el Logging Application Block de Microsoft o el Log4net de la fundación Apache.

Nuevo número de la revista de software libre Atix

Ha salido publicado un nuevo número de la revista de Software Libre Atix. Este es el número 19 de la publicación para su descarga, presionar en el enlace siguiente:

domingo, 24 de abril de 2011

Uso de custom exceptions en aplicaciones GTK# con Monodevelop

Hace unos días publiqué una entrada que mostraba el uso de excepciones personalizadas y como ejemplo de esa técnica un proyecto de consola de MonoDevelop que obtenía registros de una tabla en una base de datos PostgreSQL, derivado de ese ejemplo hice una aplicación en GTK# la cual muestra los mismos conceptos pero ahora con un formulario gráfico, por lo que hice algunas modificaciones con respecto al proyecto de consola de manera que pueda entenderse de una mejor manera.
Los pasos son:

  1. Ejecutamos MonoDevelop y creamos un proyecto GTK# llamado GtkPostException
  2. Descargamos y descomprimimos el código del ejemplo de excepciones de este enlace.
  3. Con el botón derecho encima del icono de la solución agregamos las siguientes clases a la solución.
  4. Book.cs
    BooksDataManager.cs
    DataBaseException.cs
    Logger.cs
    RuntimeException.cs
    
  5. Una vez agregadas las clases a la solución verificamos y en su caso editamos para que cada clase se agrupe dentro del namespace GtkPostException.

  6. Creamos un formulario con dos controles label, un control TextView (en donde teclearemos la Connection String), un control Treeviewdonde mostramos el resultado de la consulta y un botón de manera que el formulario tenga el aspecto visual de la siguiente imagen:

  7. Cambiamos el código de la clase BooksDataManager en el método SelectAll para que reciba un parámetro string con el valor de la Connection String recibida desde la interfaz de usuario. Aquí el listado completo de la clase:

  8. Listing 1. La clase para acceso a datos.

  9. Agregamos al evento Clicked del Botón el metodo ExecuteQuery, cuyo código se muestra a continuación:
  10. Listing 2. Evento del botón executeQuery.

  11. El listado completo de la clase MainWindow se muestra a continuación:
  12. Listing 3. Clase principal de la interfaz GTK# Custom exception.

Si se compila correctamente al ejecutar la aplicación veremos una pantalla como la siguiente imagen:

Al presionar el botón OK mostrará los registros de la tabla conforme a la consulta, como en la siguiente imagen.


Hasta aquí el flujo normal de la aplicación o happy path,ahora vamos a ocasionar una excepción reemplazando el usuario postgres por el usuario sa el cual no existe en la base de datos hecho esto presionamos el botón para que se muestre la excepción personalizada en un cuadro de dialogo, esta excepción queda a nivel base de datos de ahí que lance una DataBaseException.


Este tipo de excepciones son registradas por PostgreSQL si el proceso del servidor se ejecutó desde una terminal como en la siguiente imagen:


Para generar la excepción a nivel aplicación en vez del valor de la Connection String tecleamos cualquier cadena no válida con lo que al presionar el botón nos mostrará el mensaje referente a la clase RuntimeException como en la siguiente imagen:


El detalle a nivel técnico de las excepciones originales se podrá consultar en el archivo log.txt el cual por lo general no es accesible al usuario final sino únicamente al personal técnico.
A continuación el enlace para descargar el ejercicio completo.


sábado, 23 de abril de 2011

Uso de Custom Exceptions (excepciones personalizadas) en .NET parte II

En la entrada anterior del blog, me olvide de publicar el archivo App.config donde se encuentra la cadena de conexión (ConnectionString) y se accede a su valor mediante la siguiente línea en la aplicación y se asigna a la variable connStr.

En caso de ser necesario y de tener algún incoveniente en construir la solución aqui dejo el enlace para descargar el código fuente.


martes, 19 de abril de 2011

Uso de Custom Exceptions (excepciones personalizadas) en .NET

Las excepciones (exceptions) son un mecanismo incorporado en .NET que ayuda a la detección de condiciones excepcionales en nuestras aplicaciones, entiéndase por condiciones excepcionales aquellas situaciones que están fuera de nuestro control y que no podemos prever como programadores, esto es distinto de los errores de código y de los errores de aplicación ya que estos si son prevenibles y manejables dentro del ciclo de desarrollo. Como ejemplos de condiciones excepcionales tenemos:

  • Conectarse a una base de datos que se encuentra offline.
  • Conectarse a una base de datos y tratar de ejecutar un store procedure que no existe.
  • Leer de un archivo en disco que no se encuentra en una determinada ruta.
  • Convertir una cadena de caracteres o alfanumérica hacia un tipo numérico.
  • Ejecutar una acción cuando ya no hay memoria disponible en el sistema operativo.
  • Cuando se hace referencia a un elemento no creado o que se encuentra fuera de los límites de un arreglo.

En .NET la técnica para el manejo de estas condiciones se conoce como structured exception handling (SEH) (la traducción sería más o menos manejo estructurado de excepciones) esta técnica a base de bloques es parte integral de .NET y aplicable a cualquier lenguaje que se ejecute en .NET, en esencia parte de dos clases ApplicationException y SystemException que heredan de la clase System.Exception y que agrupan las excepciones en dos categorías, la primera representa excepciones lanzadas por las aplicaciones y la segunda las del CLR, las clases para excepciones incluidas en .NET son derivadas de estas clases que cubren los casos más comunes y para su empleo se usa la combinación de cuatro palabras reservadas: try,catch throw, finally como se detalla a continuación en los pasos siguientes:

1-. Se pone dentro de un bloque try aquel código que podría tener un evento inesperado.

try
{
         Paso1();
         Paso2();
         Paso3();
}

2-.Al final del bloque try puede ir acompañado o no de varios bloques catch, típicamente aparece la palabra catch con la clase que manejará el error si la palabra catch no especifica una clase entonces atrapara todas las posibles excepciones, cuando se enumeran una a una las posibles condiciones de excepción es MUY IMPORTANTE la jerarquía de clases ya que cada bloque debe ir de la clase más específica a la clase más general, por ejemplo:

       catch(NpgsqlException ex){  //derivada de DbExeption
       }
       catch(DbException ex){  //derivada de ExternalException
       }
       catch(ExternalException ex){ //derivada de SystemException
       }
       catch(SystemException ex){ //derivada de Exception
       }
       catch(Exception ex){ //derivada de Exception
       }

3-. Dentro de cada uno de los bloques catch se coloca el código para el manejo de la excepción como agregar nueva información a la excepción, establecer valores predeterminados de variables o lo más común aquí es tener un Log o bitácora para registrar la excepción, después hay que reenviar o arrojar la excepción a la clase que ejecuto ese código, hay dos alternativas a esto enviar la excepción tal cuál o bien enviar una excepción personalizada que sea de valor para las capas que llamaron la ejecución del código.

        catch(NpgsqlException ex){  //derivada de DbExeption
           EscribeBitacora(ex.Message); 
         }

4-. En el bloque finally se coloca el código que siempre será ejecutado haya ocurrido o no una excepción, por lo general en este bloque se coloca código de limpieza (cleanup) por ejemplo se cierran conexiones de red o a bases de datos, se liberan recursos utilizados o se cierran archivos abiertos entre otros usos.

      finally {
            CloseConnectionDB();
            CloseNetWorkConnection();
            CloseFileOpenToWrite();
         }

Las excepciones no deben ocasionar que la aplicación se colapse por completo por lo que una buena estrategia de desarrollo es manejar las excepciones y responder al evento que las ocasiono. Aunque el uso clases predeterminadas dentro del Framework es adecuado presenta una desventaja por el tipo de mensaje predeterminado ya que para el usuario esa información no debe ser de su conocimiento y en la mayoría de los casos no le encuentra utilidad, por lo que es necesario envolver la excepción dentro una técnica para personalizar excepciones.

La técnica para el uso personalizado de Excepciones

.NET permite crear excepciones personalizadas que envuelven las excepciones originales por excepciones que tengan nuevas características como por ejemplo: mensajes más significativos para el usuario, traducciones o que pueden tener información adicional.
Los pasos para crear una excepción personalizada son:

1-. Crear una nueva clase con el sufijo Exception como una buena práctica y recomendación las clases derivadas de Exception utilizan el este sufijo.

     class RuntimeException
     {
      }

2-. Derivar esta clase de ApplicationException, aunque se podría derivar directamente de Exception, no se considera una buena práctica.

     class RuntimeException : ApplicationException 
     {
      }

3-. La clase debe de tener tres constructores dos adicionales al constructor predeterminado o vacio, como se muestra en el siguiente código:

     public class RuntimeException : ApplicationException
     {
      public RuntimeException(string message): base(message){ }
      public RuntimeException(string message, Exception inner): base(message, inner){}
      }

Como ejemplo de estos conceptos vamos a crear una aplicación que se conecta a una base de datos PostgreSQL, lee y muestra los registros de una tabla llamada Books y donde hace uso de dos excepciones personalizadas: DataBaseException y RuntimeException.
En Monodevelop o en Visual Studio creamos una aplicación de consola, como en las siguientes imágenes.

Creamos una base de datos en PostgreSQL llamada curry y una tabla llamada Books que tendrá los campos como en la siguiente imagen:

Aquí el Script de creación de la tabla

Una vez creada la base de datos y el proyecto, creamos la siguiente clase de entidad.

Las clases derivadas y personalizadas que muestran con los constructores básicos del manejo de excepción.
La clase que maneja las excepciones en base de datos


La clase que maneja las excepciones de la aplicación.

La clase de acceso a datos

La clase logger que se encargara de registrar las excepciones.

Por último la clase principal en donde usamos las excepciones personalizadas.

Una vez creadas estas clases dentro del proyecto la estructura se verá más o menos así en MonoDevelop

Para ejecutar este programa siendo una aplicación de consola debemos de habilitar la salida en una terminal independiente en el menú Project del menú principal elegimos la opción Project properties donde nos aparece la siguiente ventana.

Ahí seleccionamos la opción Run on external console, ahora al ejecutar el programa el resultado se verá en una ventana terminal independiente como en la siguiente imagen que muestra el resultado sin ninguna excepción.

Sin embargo si ocurre una excepción relacionada con la base de datos se mostrará la salida como en la siguiente imagen.

Si hay una excepción en la aplicación se mostrará el resultado como en la siguiente imagen.

La técnica de custom exceptions permite la separación entre el código de flujo de la aplicación y el código de manejo de excepciones lo que hace la aplicación cumpla con un mecanismo mínimo de calidad y sea mucho más manejable, robusta y escalable.