Tratando de Ser Paciente con esta Pasión

Hola! ante todo no pretendo aburrirlos con una introducción!

Soy simplemente un desarrollador que , si encuentro un tema interesante sobre el que me parece que pueda aportar mi experiencia, trataré de sumar , tratando de hacer alguna breve reseña al respecto.

Gracias por leer!

jueves, 18 de agosto de 2016

Tres Simples formas de Evitar Usar IF en nuestro Código




Primero, quizas algunos se pregunten por qué esta mal usar IFs ?


Existen varias razones para no usarlos, pero tratare de ser preciso y consistente para luego pasar a lo que nos aplica que es como reemplezar, en la medida que se pueda esta forma de decisión:

1.- Mas allá que el "if" es la forma inicial de plantear estrategias de decisión dentro del código, estamos haciendolo de una forma es muy posible que, a menos que sean necesarios, posiblemente encapsule comportamiento que no corresponda a la clase que lo aloja y que quede en un lugar donde luego tengamos que modificar relativamente seguido si los cambios o escenarios cambian.

2.- Hablando puramente de objetos, y que el paradigma es “intereactuan los objetos solo a a travez de métodos”, el if es una forma en la cual estamos saliendo de este paradigma , para meternos en una idea mas procedural. Si quisieramos ser mas "objetosos” deberiamos salir de este tipo de implementación.

3.- Subjetivamente, El código queda feo; Saca legibilidad y aumenta las chances de errores.


Dado que java dentro de los puristas de objetos es bastante vapuleado y con razón, tratare en este articulo mostrar 3 técnicas que nos permitirán evitar los "if" en nuestro código para este lenguaje, logrando además hacerlo más mantenible.


Si bien son approachs muy especificos los que planteo, ya que decidí volcar el articulo a un hecho meramente implementativo, lo que muestro esta basado en los patrones conocidos ( Strategy, State, etc). El objetivo es que mas alla de los nombres puedan ver algunas simples ideas que mejoran la implementación.

Primero la base de Todo: Polimorfismo.



Dado que lo que queremos es tener elección de comportamiento en función de las condiciones que se den , lo que estamos queriendo hacer es hablar de unificar tipos de comportamientos bajo una jerarquia, en la cual bajo una interfaz/clase abstracta decidamos en la clase concreta implementar el comportamiento deseado.


Para mejorar esta idea nadie mejor que leer sobre el principio de Liskov, y mejor aun leer sobre SOLID.


Como ejemplo usaré este esquema, que lo iremos usando en el resto de los ejemplos. El concepto de negocio sobre el que trabajaré es:
Dado distintos tipos de clientes, queremos realizar la cobranza de los mismos.


Podemos tener Un Cliente , con los siguientes Tipos:

  • ClienteSimple
  • ClientePyme
  • ClienteGrande


Tendríamos que tener una interfaz:


public interface CobranzaClientes{
void realizarCobranza();
}


Tendriamos las siguientes implementaciones:


public class CobrarClienteSimple implements CobranzaClientes{
public void realizarCobranza(){
// Forma de Cobrar Para Clientes Simples
}
}
public class CobrarClientePyme implements CobranzaClientes{
public void realizarCobranza(){
// Forma de Cobrar Para Clientes Pyme
}
}
public class CobrarClienteGrande implements CobranzaClientes{
public void realizarCobranza(){
// Forma de Cobrar Para Clientes Grande
}
}

Ahora pasemos a las técnicas:

1.A-Estrategia por Mapas (Opción Sin Java 8)

Asumamos que en función del tipo de Cliente, por ahora un String , realizamos la tarea de cobranza según el tipo de cliente.
Es entonces la solución posible a partir de una Factory (o alguna herramienta de IOC, que nos cargue un mapa) nos proveera conceptualmente funcionalidad similar a lo siguiente:


public class FactoryCobranzas{
public static Map getMapaCobranza(){
Map cobranza = new HashMap<String,CobranzaClientes>();
cobranza.put(“ClienteSimple”, new CobranzaClienteSimple());
cobranza.put(“ClientePyme””, new CobranzaClientePyme());
cobranza.put(“ClienteGrande””, new CobranzaClienteGrande());
return cobranza;
}
}


Entonces en tu clase que recibe cada cliente para efectuar la cobranza sería, evitando los ifs:


private Map cobranzaStrategy = FactoryCobranzas.getMapaCobranza();


public void efectuarCobranzaPorCliente(Cliente cliente){
cobranzaStrategy.get(cliente.getTipo()).realizarCobranza();
}


Como vemos evitamos de hacer "if" validando el tipo , y ademas si surgiese un nuevo tipo de ciente, por el momento es solo agregar un campo mas al mapa, y no cambiamos nuestro fuente.


Notar que la Key en este caso puede ser un String, pero podría ser un Enum u otro identificador que nos permita seleccionar la ram de funcionalidad que pretendamos. Ademas notar lo útil del concepto de programar orientado a Interfaces, y no a implementaciones.


1.B-Estrategia por Mapas (Opción Con Java 8)

La clase Factory nos quedaria , mucho mas simple y sin agregar quizas el esquema de Jerarquia clases, lo cual es aplicable si a lo que se quiere aplicar es mas simple en logica :


public class FactoryCobranzas{
Function cobranzaClienteSimple = () -> ;// Hacer cosas de Cobranza Simple
Function cobranzaClientePyme = () -> ;// Hacer cosas de Cobranza Pyme
Function cobranzaClienteGrande = () -> ;// Hacer cosas de Cobranza Grande
public static Map getMapaCobranza(){
Map cobranza = new HashMap<String, Function>();
cobranza.put(“ClienteSimple”, cobranzaClienteSimple);
cobranza.put(“ClientePyme””, cobranzaClientePyme);
cobranza.put(“ClienteGrande””, cobranzaClienteSimple);
return cobranza;
}
}


Esta solucion es similar en un punto en aplicacion , aunque para mi pierde un poco en legibilidad:
cobranzaStrategy.get(cliente.getTipo()).apply();

2.-Estrategia por Reflection



Esta estrategia, ademas de un fuerte elemento de “Convention over Configuration” , basado en el mismo criterio que en el anterior, hacemos algo a mi gusto mas rustico, usando reflection,  pero no por ello deja  que evitemos usar if:


private String packageWhereStrategyExists = “com.ar.paquete.”;


public void efectuarCobranzaPorCliente(Cliente cliente){
CobranzaClientes cobranzaCliente =  (CobranzaClientes) Class.forName(packageWhereStrategyExists+ cliente.getTipoCliente()).newInstance();
cobranzaCliente.realizarCobranza();
}


Esto podria usarse tambien con Enums en el tipo:
String className = packageWhereStrategyExists+ cliente.getTipoCliente().name();
Class.forName(className).newInstance();
cobranzaCliente.realizarCobranza();


Un problema que podemos observar en este approach , es que si la clase que instanciamos posee colaboradores, los mismos no se crean ni vendran por este medio, por lo que se podria deducir que este acercamiento es para modelar logicas no muy complejas .
Un detalle que acerco es no tener miedo a la meta-programacion que se vislumbra en este ejemplo. Es una herramienta que hay que conocer y no abusar, con implementaciones mas simples en otros lenguajes ( como ruby) pero no por eso temerle por su complejidad, como ocurre en java.

3.-Estrategia Por Enums:



Si definimos un Enum de Cobranza:


public enum TipoCliente{
   ClienteSimple("ClienteSimple") {
@Override
public void realizarCobranza() {
            new CobranzaClienteSimple().realizarCobranza();
}
},
   ClientePyme("ClientePyme") {
@Override
public void realizarCobranza() {
            new CobranzaClientePyme().realizarCobranza();
}
},
   ClienteGrande("ClienteGrande") {
@Override
public void realizarCobranza() {
            new CobranzaClienteGrande().realizarCobranza();
}
};
   
//….. Otras implementaciones que necesitemos
   // Este es el secreto para que los campos del enum implementen la funcionalidad!
   public abstract void realizarCobranza();
}


En este caso no necesitamos tener la interfaz ( aunque es recomendable) y en la clase Cliente, el tipo corresponde a este Enum , entonces:
public void efectuarCobranzaPorCliente(Cliente cliente){
cliente.getTipoCliente().realizarCobranza();
}


Para este tipo de estrategia son validos hibridos como con el mapa adentro de la clase del Enum.

Conclusión



En cualquiera de los 3 escenarios planteados, nos enfocamos que la necesidad de enfrentar el uso de distintos tipos de estrategia de realizar tareas, no nos lleve a romper el paradigma de objetos, usando abusivamente la herramienta “IF”. Vemos tambien es que si nos habituamos cuando es valido a evitar este uso, usualemente lograremos un codigo mas escalable, mas limpio y focalizado en la implementación y no en cómo llamar a la implementación.


Como corolario, es bueno tambien pensar llegar a este tipo de soluciones son las herramientas finales que usamos cuando vemos que nuestros test llegaron al “verde” y nos encontramos en la etapa de mejorar implementaciones; Hacerlo antes es riesgoso, no invalido, pero si factible de sobre-implementacion.


Gracias!

lunes, 4 de abril de 2016

Mantener la sesion de ssh aún saliendo del Server, La Solución ? Screen .

Que problema tuve ?

En estos dias tuve que luchar con algunos procesos batch que los debia correr por script, algunas veces largos y el problema es ; ¿ como hacerlos si me tengo que desloguear, por ende se corta el script ?

La Solución: 

Screen permite crear instancias de bash dentro de la misma consola que estan usando , la misma permanece viva por mas que se deslogueen.

Nota: el comando directo contra Screen es Ctrl A ( es como un comando hacia la consola, como un menú)  Por comodidad lo llamare simplemente "scr"

Como funciona:

Para comenzar :

$> screen 

se crea la nueva y corran un log

Bueno ahora si desean volver a la consola original : scr + d  

Para volver : screen -x

Para crear una nueva pantalla , ya dentro de screen : scr + c

Pueden poner a correr un top

para pasar de pantalla en pantalla dentro de screen ( para adelante) : scr + n 
para pasar de pantalla en pantalla dentro de screen ( para atras) : scr + p

Si alguna la desean matar por que ya no les sirve : scr + k
( esto es por cada una que tengan viva.)

y Repito lo más util, es que si se desloguean .. cuando vuelvan a loguearse , lo que tenian en tal pantalla de screen , seguira corriendo.


Para que sirve ?
* Para correr scripts , varios.
* Acceder directo a un log que permanezca corriendo en vez del tail


Que disfruten!

sábado, 6 de febrero de 2016

Preocuparnos por la estimación nos vuelve mejores desarrolladores



Creo que se ha escrito mucho sobre estimación de tareas. Metodologías, formas , intenciones, colores, juegos de Azar. Mucho.
Ahora , no he leido todavía las razones por las cuales nos conviene estimar bien a nosotros, los que estamos involucrados mas directamente en el proceso de construcción de software.

Pero primero, qué entiendo por estimación? al menos desde el punto de vista de sistemas; es predecir en función de nuestro buen juicio sobre ciertas tareas que estamos realizando cuanto tiempo nos demandará realizarlas. Debido a que justamente nuestro "buen juicio" es un atributo muy maleable, se han creado distintos métodos para darles una conciencia de mensurabilidad a este criterio ó "buen juicio".


Aparte es sumamente influenciable por cuestiones internas ( "Hoy tengo ganas de trabajar ? ", "Discuti con mi novia / esposa /hijos / padres", "Mi jefe es o se hace, que lo codee él", etc, etc) y cuestiones externas ( "esto tiene que ser entregado para ayer, como venimos ?", "Aparecio un cliente mas, que nos dará un incremento en el tráfico de la herramienta del 500%; estamos preparados para manejar este flujo? deberíamos...", "El proceso requiere para que evolucione mejor un refactor, ya que nos dimos cuenta que esta grilla la vamos a reutilizar, conviene  que sea mas inteligente").

Dados los factores , externos e internos, impactan en un alto grado de falla en nuestro buen juicio en cuanto a los tiempos de implementación, lease estimación y además, son demasiados. Es necesario como desarrolladores , asi como conocer nuevas herramientas y frameworks ( es un must para mantenernos actualizados) debemos conocer esta herramienta soft de la cual somos únicos responsables de mejorarla y conocer sus alternativas.

Solo nosotros conocemos lo que nos  implica llegar a un deadline, debemos poder en un punto poder conocer que grado de imprevisibilidad nos manejamos, y al menos manejar, dar visibilidad sobre los riesgos de dicha cuestión. Saber que nos implica hacer algo y que factores nos afectan nos podrán dar la capacidad para desenvolvernos independientes y la fiabilidad de las personas en quien confía en nosotros un desarrollo.

Una vez que entendemos los factores , deberíamos tener y haber logrado mucha mas firmeza y claridad a la hora de defender nuestra estimación.Para poder defenderla y hacerla menos discutible, debemos entender todos los temas que nos afectan, y eso nos llevará a lograr ganarnos esa confianza.

Nuestra estimación debe estar fuertemente basada en argumentos. No podemos hablar sin fundamentos. No podemos validar nuestra postura sin un test que lo avale; ups ! perdón , se me escapo el TDD developer que soy.. pero , acaso ..no es así ?; Entonces busquemos firmeza en nuestras aseveraciones conociendo y hechando mano a que cuestiones nos afectan para poder ser mejores estimando. Porque cuando mejores somos estimando, mejores desarrolladores somos ( al menos , entre las infinitas cualidades que existen para hablar de ser un buen desarrollador, habremos ganado una muy intangible y poderosa ), ya que conocemos claramente el alcance de hacia donde iremos/llegamos, que impacto vamos a tener en nuestro producto y a futuro mas dueños de nuestras decisiones en cuanto a tiempos.

Que temas nos dan una idea de que sabemos con que grado estamos estimando ?

Voy a tratar de desarrollar en items temas que para mi punto de vista nos dan una evaluación para nosotros del  grado de la calidad de nuestras estimaciones; En la medida que mejor las manejemos, más cerca estaremos de ser precisos.

1.-  Grado de Switch context:
Debemos tener en cuenta siempre el contexto en el que nos movemos. A medida que poseamos mas Señority en nuestra profesión, se nos demandará que estemos menos enfocado en las cuestiones propias de la construcción del proyecto para empezar a ser los referentes de otros ó prestar atención a otros Issues no relacionados al proyecto ( ni hablar si estamos en varios proyectos ). Esto nos afecta claramente la estimación. Tener en cuenta cuanto tiempo pasamos en reuniones antes que enfocados en cómo dar eficiencias al algoritmos, nos da gimnasia en otros temas, pero nos saca foco , tiempo y destreza en esto puntualmente que nos demandan. A la hora de estimar, tener claro esto , y no denostarlo.


2.- Calidad del Producto:
Otra vez calidad ? Hay muchos libros, pienso en "Clean Code" y listo. Pero aquí apunto a lo que cada uno de nosotros entiende por calidad. Si no entendemos el concepto del vamos estamos en problemas y tenemos que empezar a adquirir tal concepto. Si lo entendemos y sabemos por donde pasa, entonces llegamos un punto a saber el status de nuestra "mejor" obra y/ó por donde pasa algo que directamente es una bomba de tiempo. El punto es tener muy claro la deuda técnica que dejamos.

Yo encuentro estos 4 niveles , de peor calidad a mayor:
1.- Código sin testear , con metodos de mas de 20 lineas, y poca modularizacion, Muy acoplado.
2.- Código  testeado, reproducible , aunque con muchas falencias de diseño. Si estamos en objetos se carateriza por muuuchos Ifs.
3.- Testeado , modularizado de a partes, pero con cosas en algún punto repetidas. con funcionalidad de mas.
4.- Testeado. Se usó TDD. Hay los patrones Justos. Arquitectura hexagonal. Una belleza.

En general cada uno de estos niveles, entender los pros y cons, van de la mano del Señority, pero a veces tambien implican mas tiempo. o estamos en un transcurrir del punto 2 al punto 4 ( El refactor de TDD ). Si supimos diferenciar bien nuestros commits, sabremos a que punto llegaremos con tal tiempo. Si la calidad es un compromiso, los commits debe ser nuestra firma. Para poder dar signos de esa firma debemos saber cuando llegaremos a cada compromiso. Esto debe ser claro a la hora de estimar. Cuando la calidad y los puntos de fallos sean casi minimos, contra primeras estimaciones y entregas, es que empezaremos a ganarnos nuestra confianza no solo de nosotros si no de quienes usan nuestro código; Este es el punto donde empezamos a lograr que nuestra estimación no sea cuestionable.

3.- Visibilidad de las tareas, Ser inquisitivos sobre el alcance del proyecto:
Debemos saber qué se pretende y adonde queremos llegar. Trabajar en modo autómata decididamente nos resta a nuestra capacidad. En esta postura solo desarrollamos cosas que no sabemos como funcionan del todo dentro de un contexto de un todo ; Queremos ser un asset que sume al equipo ? indudablemente debemos saber que hacemos y que impacto tiene nuestro trabajo. En función de eso sabremos que grado de entrega haremos , por ende estimaremos mas precisos en función de la necesidad del cliente.


Entiendo que esta lista podría alargarse muchísimo más; ya que la pregunta de fondo que nos podemos hacer es : ¿ Qué cosas son las que nos hacen mejores profesionales en It, y que a la vez nos dejan satisfechos a nosotros con nuestro propio desempeño ? Pero esto es tema quizas de otro post. Por ahora me quedo con la intención , con las ganas de haber logrado que vos , que día a día lidias con la incertidumbre de no saber cuando finalizas tus tareas, apuntes cada día a poder lograr ser más preciso y confiado con tus estimaciones.

Para finalizar, como una vez escuche a una persona que desde mi punto de vista entendía muchísimo por donde venía la mano: " Siempre , no importa el contexto, siempre se puede estimar". Yo agrego: "La precisión nos la dá el contexto"

Gracias!








lunes, 21 de diciembre de 2015

Batallas con un Proyecto Con Elastic Search

Hace aproximadamente 1 año estoy liderando un proyecto de migración de APIS para un Front end de Facturación.

Las APIs al principio fuimos contra la Base, y cuando vimos que los tiempos claramente no eran los esperados ( luego supimos que unos de los requerimientos de las APIS era que su performance por request no deberían superar los 0.1 seg) tuvimos que implementar una solución de Elastic Search.

Partimos de un esquema de arquitectura que nos permitiese no solo la carga Online de manera escalable, si no que ademas pudiesemos hacer cargas de datos históricos, nosotros los llamamos "Cargas Iniciales", para que el usuario pueda ver su histórico de Facturación.

En ese tiempo descubrimos una serie de cuestiones y experiencias con ES que me pareció interesante compartirlo con uds.

* El modelado de ES a nivel de cluster es simplemente caprichoso. No existe una receta que nos indique cuál es el mejor modelado. Si podemos saber que los indices , cuando son muy grandes, requieren una gran cantidad de Yardas para que performe ( una yarda se desempeña de manera optima con hasta 10G de información. Nosotros llegamos a un esquema de más de 100 yardas).
El modelado iterativo sucederá inevitablemente ; para ello tener en claro los modelos de Procesos de Cargas, ya que lo tendremos que rehacer varias veces a nuestro cluster hasta que la performance sea la esperada.

* Rootear es fundamental. Los tiempos se mejoran mucho si se agrega este parámetro. Puede haber indicio de como armemos nuestros indices no lo necesitan. Pero si necesitan velocidad en cientos de gigas, esto es necesario

* El Scroll es una herramienta que no debemos olvidar.Para hacer consultas paginadas donde se necesita recolectar cientos de datos , pero no queremos penalizar la performance, hay que usarlo.

* Programar OO y TDD ayuda, y mucho. Code Reviews tambien. ES es una herramienta poderosa. Pero Armar las consultas pueden ser por demás engorrosas y muchas veces poco elegantes. El esquema de builders que utiliza no es el mas bello. Por lo que conviene ser prolijo a la hora de diseñar, y conocer claramente que escenarios queremos soportar.

* Las modificaciones no son baratas: tratemos siempre de entender claramente los escenarios funcionales. Si bien un modelo iterativo es el que mas me agrada, una falla de concepción del requerimiento del usuario impacta muchas veces en rehacer el indice, o por que no todo el elastic. Tener claro los conceptos ( para tener claros los test ) tendrá un claro impacto en nuestro ES. Saber cuantos usuarios lo usaran , cuanto volumen aproximado, cual es la consulta que mas nos importa son "hints" importantísimos para poder hacer un modelo en el cual las modificaciones no nos peguen de lleno y nos hagan re hacer la infraestructura.

* Juniors... con cuidado!. Parece tan simple usar elastic, parece fácil, pero no. Requiere idealmente que los desarrolladores sean maduros para encarar el uso de la herramienta. Desde la concepción Funcional del problema hasta donde implementar la solución es fundamental tener a alguien que pueda entender desde la infraestructura hasta los vericuetos funcionales del problema. Esto que menciono, tan abstracto, es lo que realmente lo hace facil y escalable. Cualquier falla en esto implicará que los diseños no sean los mejores, que los refactors sean en extremo costosos. Creo igualmente que esto en un punto aplica a cualquier proyecto. Tener un buen "counselor" en esto es casi menester. Si no, deberemos saber que haremos multiples refactors hasta lograr la implementación que responda a nuestros requerimientos siempre será cuestión de varias iteraciones.

* Migrar de Elastic , de versiones , sobre todo en productivos; con mucho cuidado. Es factible migrar datos de un Elastic a otro ( nos toco hacerlo de una versión 1.4 a una versión 1.7 ) . Hay que tener mucho cuidado con la situación de datos que vienen online y el pisado de datos que vienen desde el "otro" Elastic. Concurrencia es un gran Issue y riesgo a tener en cuenta.



Este es el enfoque desde un equipo que se luchó/lucha para lograr tener productivo un esquema con ES como motor de datos.

Si alguien tiene otras experiencias , con gusto escuchare. Si me acuerdo mas diatribas sobre lo mismo , Tambien lo volcaré.

Gracias por Pasar.








lunes, 30 de noviembre de 2015

Pensar en patrones esta mal al inicio de un problema.

Siempre defendi los patrones de diseño. Para mi programar sin patrones de diseño es como querer ser mecánico de autos, sin haber hecho cursos al respecto. Que quiero decir ? Es posible que logres arreglar el auto, pero seguramente no será la mejor solución o serán soluciones fruto de la intuición y la experiencia en ese modelo ( a veces esto es mejor, pero estamos hablando de adivinar soluciones, intuir soluciones, vs entender el diagnostico y resolverlo basado en conceptos adquiridos).

Claramente con cosas nuevas te encontraras con nuevas situaciones las cuales quizás por aplicar soluciones ya vistas hagas entrar un circulo en un cuadrado, pero a la larga tendrás otros problemas. ( El que tuvo autos usados , donde mecánicos de la experiencia se los han arreglado.. sabe de lo que hablo ).

Los patrones te dan eso; que puedas pensar varios tipos de soluciones al respecto y encontrar de acuerdo a tu conocimiento del contexto, la llave, el destornillador adecuada que mejor convenga.

Espera un poquito; mi titulo dice "Pensar en patrones esta mal al inicio de un problema", como es entonces ? Por que la contradicción ?

No hay contradicción si sos un desarrollador TDD. Cuando arrancas un problema bajo el concepto "red, green,  refactor" , tu único objetivo es lograr que la suites de problemas a los que pensás resolver estarán ok en la medida que vos puedas pasar de red a green, sin pre conceptos y sin arbitrariedades y , por sobre todas las cosas sin " por las dudas ". Tu única meta es que los test pasen.

OK. Es por eso que concluyo que pensar en patrones antes de implementar los test te sesgan a escenarios donde quizás estas haciendo por las dudas, entonces estas desarrollando cosas que quizás solucionen problemas que nunca ocurran, ó abriras puertas a escenarios que nunca se den; esto implica perdida de tiempo

Creo que el buen criterio seria aplicar la idea de patrones una vez que todos tus test pasan al estado "green", recién ahí empezás a pensar en que patron se adecua a la solución implementada. En ese momento, quizás el momento de la piedra libre a tus cualidades de razonamiento ( la parte divertida ) dadas las situaciones de problemas que investigaste para plantear tus escenarios de test, entendés por que será mejor un command, que un strategy ó un state pattern. Por que claramente usarías una factory o un builder, ó por que recién ahora conviene un mediator en lugar de un Facade, como presupusiste.

Buscar ser un eficiente TDD developer te llevará sin duda a ser un mejor Pattern oriented developer. Pensar para que vas usar el auto , te lleva ver como lo diseñas. Pensar algo que sirve por default para todas las soluciones no solo te cierra posibles contextos en los cuales quedes desajustado, si no que te lleve a implementar complejidades innecesarias que solamente te lleven a test innecesarios; Esta es la situación donde los que "desacreditan" TDD tienen razón para protestar por la productividad. Si hacemos cosas de mas , claramente le robamos tiempo  a las cosas que si son necesarias.

La economía de recursos no implica malos diseños , implica Mejores diseños, es por eso que resolver los problemas con las ideas y herramientas adecuadas depende mucho de como se implemente mas alla de saber, ahora comparando con el ajedrez, cómo mover las piezas.

jueves, 12 de febrero de 2015

Ejemplo de como funciona el Kill -3 (QUIT) para procesos Java

Esto me parecio interesante de compartir:

Dada esta clase Stopping.java ( meterlo todo en un unico archivo, más facil para probar este ejemplo.):

import java.util.Timer;
import java.util.TimerTask;

class CanStop extends Thread {
   private volatile boolean stop = false;
   private int counter = 0;
   public void run() {
      while (!stop && counter < 10000) {
         System.out.println(counter++);
         try{
         Thread.sleep(1000);
        } catch (Exception e){
         System.out.println(e);
        }
      }
      if (stop)
      System.out.println("Detected stop");
   }
   public void requestStop() {
      stop = true;
   }
}
public class Stopping {
   public static void main(String[] args) {
      final CanStop stoppable = new CanStop();
      stoppable.start();
         try{
         Thread.sleep(20000);
        } catch (Exception e){
         System.out.println(e);
        }
      new Timer(true).schedule(new TimerTask() {
         public void run() {
            System.out.println("Requesting stop");
            stoppable.requestStop();
         }
}, 350);
   }
}


Uds si la ejecutan , contara hasta 20 y se detendrá ( javac Stopping.java para compilar y luego para ejecutar java Stopping ), de paso el ejemplo de como detener un hilo desde afuera.. de manera muuuy simple.

Ahora, a lo nuestro; en otra consola ( esto es en un unix , el contexto de la mayoría de procesos java )... mientras se ejecuta el proceso de Stopping,  pueden hacer:

ps -fea | grep Stopping

Que tiro en mi caso? :

711670932 29342 28674   0 12:41PM ttys001    0:00.16 /usr/bin/java Stopping

les va a tirar el pid del Proceso y luego

kill -3 29342

y van a ver el dump del proceso corriendo..

0
1
2
3
4
5
6
7
8
9
10
2015-02-12 12:41:12
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.45-b08 mixed mode):

"Thread-0" prio=5 tid=0x00007fce5a005000 nid=0x4f03 waiting on condition [0x000000011beb5000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at CanStop.run(Stopping.java:11)

"Service Thread" daemon prio=5 tid=0x00007fce5a813800 nid=0x4b03 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=5 tid=0x00007fce5a813000 nid=0x4903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=5 tid=0x00007fce59804800 nid=0x4703 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=5 tid=0x00007fce5a800800 nid=0x4503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=5 tid=0x00007fce5a00a000 nid=0x3103 in Object.wait() [0x000000011b6f3000]
   java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007aaa85568> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000007aaa85568> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)

"Reference Handler" daemon prio=5 tid=0x00007fce5a007000 nid=0x2f03 in Object.wait() [0x000000011b5f0000]
   java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007aaa850f0> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x00000007aaa850f0> (a java.lang.ref.Reference$Lock)

"main" prio=5 tid=0x00007fce5900c800 nid=0x1903 waiting on condition [0x000000010d366000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Stopping.main(Stopping.java:28)

"VM Thread" prio=5 tid=0x00007fce5904d000 nid=0x2d03 runnable

"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fce59018000 nid=0x2503 runnable

"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fce59803000 nid=0x2703 runnable

"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fce59803800 nid=0x2903 runnable

"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fce59018800 nid=0x2b03 runnable

"VM Periodic Task Thread" prio=5 tid=0x00007fce59830000 nid=0x4d03 waiting on condition

JNI global references: 109

Heap
 PSYoungGen      total 76800K, used 2642K [0x00000007aaa80000, 0x00000007b0000000, 0x0000000800000000)
  eden space 66048K, 4% used [0x00000007aaa80000,0x00000007aad14828,0x00000007aeb00000)
  from space 10752K, 0% used [0x00000007af580000,0x00000007af580000,0x00000007b0000000)
  to   space 10752K, 0% used [0x00000007aeb00000,0x00000007aeb00000,0x00000007af580000)
 ParOldGen       total 174592K, used 0K [0x0000000700000000, 0x000000070aa80000, 0x00000007aaa80000)
  object space 174592K, 0% used [0x0000000700000000,0x0000000700000000,0x000000070aa80000)
 PSPermGen       total 21504K, used 2551K [0x00000006fae00000, 0x00000006fc300000, 0x0000000700000000)
  object space 21504K, 11% used [0x00000006fae00000,0x00000006fb07dcb0,0x00000006fc300000)

11
12
13
14
15
16
17

Interesante cuando uno esta probando procesos concurrentes en java..