Desarrolla software con una de las empresas más grandes del mundo


Walmart recibe cada semana cerca de 270 millones de clientes y miembros que visitan más de 11,700 tiendas dentro de 65 marcas en 28 países y sitios de eCommerce.

Esto la hace la empresa más importante en ingresos a nivel mundial, si estas buscando un lugar nuevo donde trabajar, con retos constantes y con proyección internacional esta oferta es justo lo que estas buscando.

¿Qué es Walmart GTS?

Walmart GTS (Global technology services) es una oficina de Walmart enfocada en el desarrollo de aplicaciones, innovación y aplicación de tecnología para solucionar los problemas de nuestros clientes a nivel global.

Nuestra oficina no se enfoca solo en el mercado de México o Estados Unidos, sino que busca crear soluciones que se puedan aplicar en cualquier parte del mundo. Esta oficina se rige sobre metodologías ágiles, DevOps y mejora continua, por lo que no encontrarás limitantes burocráticas al momento que desarrollas tus aplicaciones.

Estas listo para unirte al equipo de ingenieros de Walmart ?

workatwalmart

Buscamos constantemente personas que busquen unirse a nuestro increíble equipo de ingenieros.

Conocimientos técnicos

Buscamos desarrolladores apasionados por el desarrollo de software con conocimientos como :

1CONOCIMIENTOSTECNICOS

Capacidades requeridas

Porque no solo la parte técnica es importante, buscamos personas que también cuenten con las siguientes capacidades:

  • Trabajo en equipo
  • Actitud positiva
  • Autodidacta
  • Buena comunicación

Idiomas

Walmart tiene tiendas y oficinas a lo largo de todo el mundo y en nuestra oficina trabajamos para todas ellas por eso es importante contar con un buen dominio de los idiomas Inglés y Español.

Si no sabes inglés pregunta si la vacante que buscas lo puede permitir, posiciones iniciales permiten aceptar ingenieros sin conocimiento en inglés y ¡Nosotros te damos las clases!.

Ofrecemos

Trabajar en Walmart te dará muchos beneficios, entre los que se encuentran:

  • Sueldo 100% Nómina contratación directa con Walmart
  • Sueldos competitivos
  • Prestaciones superiores a las de la ley
  • Retos profesionales constantes
  • Uso de nuevas tecnologías
  • Capacitación constante presencial y con plataformas en línea

Mándanos tus datos para comentarte el paquete completo de prestaciones !

capacitacion

¿Cómo puedo aplicar?

Si deseas aplicar a alguna de nuestras posiciones solo debes enviar tu CV actualizado ó el link a tu perfil de LinkedIn a la dirección contacto@devs4j.com colocando en el asunto la vacante a la que deseas aplicar “FULL STACK DEVELOPER”, “JAVA DEVELOPER” o “JAVASCRIPT DEVELOPER”.

¡Se parte de uno de los centros tecnológicos más importantes de México !

Kafka streams : Empaquetando nuestra aplicación


En el post anterior Kafka streams: Primera aplicación con Kafka streams aprendimos a programar nuestra primer aplicación Kafka streams y ejecutarla desde nuestra computadora, pero cuando creemos una aplicación que funcionará en producción buscamos que se ejecute en un servidor, así que en este post veremos como empaquetarla para que se ejecute en un cluster de Kafka.

Paso 1 Crear un fat Jar

Las aplicaciones Java se empaquetan en archivos llamados Jar(Java Archive), el problema es que el proceso default para construir estos archivos es incluir solo el código de nuestra aplicación y no todas sus dependencias (kafka-streams, slf4j, etc.).

Un fat Jar es un archivo Jar que incluye el código de nuestra aplicación pero también todas las dependencias que necesita, esto nos permite ejecutarlo en cualquier lugar ya que contiene todo lo que necesita.

Veamos el siguiente pom.xml, podemos ver que se incluyó un plugin de maven que nos permitirá generar nuestro fat jar en el que especificamos cuál será nuestra clase principal.

Hecho esto simplemente ejecutaremos el siguiente comando desde nuestra terminal:

mvn clean package

Probando nuestro fat Jar

Si queremos probar nuestro fat jar simplemente debemos ejecutar el siguiente comando desde la terminal:

 java -jar target/kafka-streams-example-0.0.1-SNAPSHOT-jar-with-dependencies.jar

Salida:

INFO StreamsConfig values:
	application.id = stream-app
	application.server =
	bootstrap.servers = [127.0.0.1:9092]
	buffered.records.per.partition = 1000
	cache.max.bytes.buffering = 10485760
	client.id =
	commit.interval.ms = 30000
	connections.max.idle.ms = 540000
	default.key.serde = class org.apache.kafka.common.serialization.Serdes$StringSerde
	default.timestamp.extractor = class org.apache.kafka.streams.processor.FailOnInvalidTimestamp
	default.value.serde = class org.apache.kafka.common.serialization.Serdes$StringSerde
	key.serde = null
	metadata.max.age.ms = 300000
	metric.reporters = []
	metrics.num.samples = 2
	metrics.recording.level = INFO
	metrics.sample.window.ms = 30000
	num.standby.replicas = 0
	num.stream.threads = 1
	partition.grouper = class org.apache.kafka.streams.processor.DefaultPartitionGrouper
	poll.ms = 100
	processing.guarantee = at_least_once
	receive.buffer.bytes = 32768
	reconnect.backoff.max.ms = 1000
	reconnect.backoff.ms = 50
	replication.factor = 1
	request.timeout.ms = 40000
	retry.backoff.ms = 100
	rocksdb.config.setter = null
	security.protocol = PLAINTEXT
	send.buffer.bytes = 131072
	state.cleanup.delay.ms = 600000
	state.dir = /tmp/kafka-streams
	timestamp.extractor = null
	value.serde = null
	windowstore.changelog.additional.retention.ms = 86400000
	zookeeper.connect =
 (org.apache.kafka.streams.StreamsConfig:223)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating consumer client (org.apache.kafka.streams.processor.internals.StreamThread:473)
INFO ConsumerConfig values:
	auto.commit.interval.ms = 5000
	auto.offset.reset = earliest
	bootstrap.servers = [127.0.0.1:9092]
	check.crcs = true
	client.id = stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1-consumer
	connections.max.idle.ms = 540000
	enable.auto.commit = false
	exclude.internal.topics = true
	fetch.max.bytes = 52428800
	fetch.max.wait.ms = 500
	fetch.min.bytes = 1
	group.id = stream-app
	heartbeat.interval.ms = 3000
	interceptor.classes = null
	internal.leave.group.on.close = false
	isolation.level = read_uncommitted
	key.deserializer = class org.apache.kafka.common.serialization.ByteArrayDeserializer
	max.partition.fetch.bytes = 1048576
	max.poll.interval.ms = 2147483647
	max.poll.records = 1000
	metadata.max.age.ms = 300000
	metric.reporters = []
	metrics.num.samples = 2
	metrics.recording.level = INFO
	metrics.sample.window.ms = 30000
	partition.assignment.strategy = [org.apache.kafka.streams.processor.internals.StreamPartitionAssignor]
	receive.buffer.bytes = 65536
	reconnect.backoff.max.ms = 1000
	reconnect.backoff.ms = 50
	request.timeout.ms = 305000
	retry.backoff.ms = 100
	sasl.jaas.config = null
	sasl.kerberos.kinit.cmd = /usr/bin/kinit
	sasl.kerberos.min.time.before.relogin = 60000
	sasl.kerberos.service.name = null
	sasl.kerberos.ticket.renew.jitter = 0.05
	sasl.kerberos.ticket.renew.window.factor = 0.8
	sasl.mechanism = GSSAPI
	security.protocol = PLAINTEXT
	send.buffer.bytes = 131072
	session.timeout.ms = 10000
	ssl.cipher.suites = null
	ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
	ssl.endpoint.identification.algorithm = null
	ssl.key.password = null
	ssl.keymanager.algorithm = SunX509
	ssl.keystore.location = null
	ssl.keystore.password = null
	ssl.keystore.type = JKS
	ssl.protocol = TLS
	ssl.provider = null
	ssl.secure.random.implementation = null
	ssl.trustmanager.algorithm = PKIX
	ssl.truststore.location = null
	ssl.truststore.password = null
	ssl.truststore.type = JKS
	value.deserializer = class org.apache.kafka.common.serialization.ByteArrayDeserializer
 (org.apache.kafka.clients.consumer.ConsumerConfig:223)
INFO Kafka version : 0.11.0.0 (org.apache.kafka.common.utils.AppInfoParser:83)
INFO Kafka commitId : cb8625948210849f (org.apache.kafka.common.utils.AppInfoParser:84)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating restore consumer client (org.apache.kafka.streams.processor.internals.StreamThread:483)
INFO ConsumerConfig values:
	auto.commit.interval.ms = 5000
	auto.offset.reset = earliest
	bootstrap.servers = [127.0.0.1:9092]
	check.crcs = true
	client.id = stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1-restore-consumer
	connections.max.idle.ms = 540000
	enable.auto.commit = false
	exclude.internal.topics = true
	fetch.max.bytes = 52428800
	fetch.max.wait.ms = 500
	fetch.min.bytes = 1
	group.id =
	heartbeat.interval.ms = 3000
	interceptor.classes = null
	internal.leave.group.on.close = false
	isolation.level = read_uncommitted
	key.deserializer = class org.apache.kafka.common.serialization.ByteArrayDeserializer
	max.partition.fetch.bytes = 1048576
	max.poll.interval.ms = 2147483647
	max.poll.records = 1000
	metadata.max.age.ms = 300000
	metric.reporters = []
	metrics.num.samples = 2
	metrics.recording.level = INFO
	metrics.sample.window.ms = 30000
	partition.assignment.strategy = [class org.apache.kafka.clients.consumer.RangeAssignor]
	receive.buffer.bytes = 65536
	reconnect.backoff.max.ms = 1000
	reconnect.backoff.ms = 50
	request.timeout.ms = 305000
	retry.backoff.ms = 100
	sasl.jaas.config = null
	sasl.kerberos.kinit.cmd = /usr/bin/kinit
	sasl.kerberos.min.time.before.relogin = 60000
	sasl.kerberos.service.name = null
	sasl.kerberos.ticket.renew.jitter = 0.05
	sasl.kerberos.ticket.renew.window.factor = 0.8
	sasl.mechanism = GSSAPI
	security.protocol = PLAINTEXT
	send.buffer.bytes = 131072
	session.timeout.ms = 10000
	ssl.cipher.suites = null
	ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
	ssl.endpoint.identification.algorithm = null
	ssl.key.password = null
	ssl.keymanager.algorithm = SunX509
	ssl.keystore.location = null
	ssl.keystore.password = null
	ssl.keystore.type = JKS
	ssl.protocol = TLS
	ssl.provider = null
	ssl.secure.random.implementation = null
	ssl.trustmanager.algorithm = PKIX
	ssl.truststore.location = null
	ssl.truststore.password = null
	ssl.truststore.type = JKS
	value.deserializer = class org.apache.kafka.common.serialization.ByteArrayDeserializer
 (org.apache.kafka.clients.consumer.ConsumerConfig:223)
INFO Kafka version : 0.11.0.0 (org.apache.kafka.common.utils.AppInfoParser:83)
INFO Kafka commitId : cb8625948210849f (org.apache.kafka.common.utils.AppInfoParser:84)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] State transition from CREATED to RUNNING. (org.apache.kafka.streams.processor.internals.StreamThread:980)
INFO stream-client [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649] State transition from CREATED to RUNNING. (org.apache.kafka.streams.KafkaStreams:229)
INFO stream-client [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649] Started Kafka Stream process (org.apache.kafka.streams.KafkaStreams:453)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Starting (org.apache.kafka.streams.processor.internals.StreamThread:523)
INFO Discovered coordinator 192.168.14.141:9092 (id: 2147483647 rack: null) for group stream-app. (org.apache.kafka.clients.consumer.internals.AbstractCoordinator:597)
INFO Revoking previously assigned partitions [] for group stream-app (org.apache.kafka.clients.consumer.internals.ConsumerCoordinator:419)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] at state RUNNING: partitions [] revoked at the beginning of consumer rebalance.
	current assigned active tasks: []
	current assigned standby tasks: []
 (org.apache.kafka.streams.processor.internals.StreamThread:205)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] State transition from RUNNING to PARTITIONS_REVOKED. (org.apache.kafka.streams.processor.internals.StreamThread:980)
INFO stream-client [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649] State transition from RUNNING to REBALANCING. (org.apache.kafka.streams.KafkaStreams:229)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Updating suspended tasks to contain active tasks [] (org.apache.kafka.streams.processor.internals.StreamThread:1400)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Removing all active tasks [] (org.apache.kafka.streams.processor.internals.StreamThread:1407)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Removing all standby tasks [] (org.apache.kafka.streams.processor.internals.StreamThread:1421)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] partition revocation took 1 ms.
	suspended active tasks: []
	suspended standby tasks: []
	previous active tasks: []
 (org.apache.kafka.streams.processor.internals.StreamThread:227)
INFO (Re-)joining group stream-app (org.apache.kafka.clients.consumer.internals.AbstractCoordinator:432)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Constructed client metadata {d85064bc-a649-43bd-b74a-ba6fc5f51649=ClientMetadata{hostInfo=null, consumers=[stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1-consumer-c55d36b5-a9ba-4832-aa11-c3469137c7ab], state=[activeTasks: ([]) standbyTasks: ([]) assignedTasks: ([]) prevActiveTasks: ([]) prevAssignedTasks: ([0_0, 0_1, 1_0, 1_1]) capacity: 1]}} from the member subscriptions. (org.apache.kafka.streams.processor.internals.StreamPartitionAssignor:316)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Completed validating internal topics in partition assignor (org.apache.kafka.streams.processor.internals.StreamPartitionAssignor:672)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Completed validating internal topics in partition assignor (org.apache.kafka.streams.processor.internals.StreamPartitionAssignor:672)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Assigned tasks to clients as {d85064bc-a649-43bd-b74a-ba6fc5f51649=[activeTasks: ([0_0, 0_1, 1_0, 1_1]) standbyTasks: ([]) assignedTasks: ([0_0, 0_1, 1_0, 1_1]) prevActiveTasks: ([]) prevAssignedTasks: ([0_0, 0_1, 1_0, 1_1]) capacity: 1]}. (org.apache.kafka.streams.processor.internals.StreamPartitionAssignor:493)
INFO Successfully joined group stream-app with generation 8 (org.apache.kafka.clients.consumer.internals.AbstractCoordinator:399)
INFO Setting newly assigned partitions [input-topic-0, input-topic-1, stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-1, stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-0] for group stream-app (org.apache.kafka.clients.consumer.internals.ConsumerCoordinator:262)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] at state PARTITIONS_REVOKED: new partitions [input-topic-0, input-topic-1, stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-1, stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-0] assigned at the end of consumer rebalance.
	assigned active tasks: [0_0, 0_1, 1_0, 1_1]
	assigned standby tasks: []
	current suspended active tasks: []
	current suspended standby tasks: []
	previous active tasks: [] (org.apache.kafka.streams.processor.internals.StreamThread:160)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] State transition from PARTITIONS_REVOKED to ASSIGNING_PARTITIONS. (org.apache.kafka.streams.processor.internals.StreamThread:980)
INFO stream-client [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649] State transition from REBALANCING to REBALANCING. (org.apache.kafka.streams.KafkaStreams:229)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Adding assigned tasks as active {0_0=[input-topic-0], 0_1=[input-topic-1], 1_0=[stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-0], 1_1=[stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-1]} (org.apache.kafka.streams.processor.internals.StreamThread:1280)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating active task 0_0 with assigned partitions [[input-topic-0]] (org.apache.kafka.streams.processor.internals.StreamThread:1229)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating shared producer client (org.apache.kafka.streams.processor.internals.StreamThread:1263)
INFO ProducerConfig values:
	acks = 1
	batch.size = 16384
	bootstrap.servers = [127.0.0.1:9092]
	buffer.memory = 33554432
	client.id = stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1-producer
	compression.type = none
	connections.max.idle.ms = 540000
	enable.idempotence = false
	interceptor.classes = null
	key.serializer = class org.apache.kafka.common.serialization.ByteArraySerializer
	linger.ms = 100
	max.block.ms = 60000
	max.in.flight.requests.per.connection = 5
	max.request.size = 1048576
	metadata.max.age.ms = 300000
	metric.reporters = []
	metrics.num.samples = 2
	metrics.recording.level = INFO
	metrics.sample.window.ms = 30000
	partitioner.class = class org.apache.kafka.clients.producer.internals.DefaultPartitioner
	receive.buffer.bytes = 32768
	reconnect.backoff.max.ms = 1000
	reconnect.backoff.ms = 50
	request.timeout.ms = 30000
	retries = 10
	retry.backoff.ms = 100
	sasl.jaas.config = null
	sasl.kerberos.kinit.cmd = /usr/bin/kinit
	sasl.kerberos.min.time.before.relogin = 60000
	sasl.kerberos.service.name = null
	sasl.kerberos.ticket.renew.jitter = 0.05
	sasl.kerberos.ticket.renew.window.factor = 0.8
	sasl.mechanism = GSSAPI
	security.protocol = PLAINTEXT
	send.buffer.bytes = 131072
	ssl.cipher.suites = null
	ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
	ssl.endpoint.identification.algorithm = null
	ssl.key.password = null
	ssl.keymanager.algorithm = SunX509
	ssl.keystore.location = null
	ssl.keystore.password = null
	ssl.keystore.type = JKS
	ssl.protocol = TLS
	ssl.provider = null
	ssl.secure.random.implementation = null
	ssl.trustmanager.algorithm = PKIX
	ssl.truststore.location = null
	ssl.truststore.password = null
	ssl.truststore.type = JKS
	transaction.timeout.ms = 60000
	transactional.id = null
	value.serializer = class org.apache.kafka.common.serialization.ByteArraySerializer
 (org.apache.kafka.clients.producer.ProducerConfig:223)
INFO Kafka version : 0.11.0.0 (org.apache.kafka.common.utils.AppInfoParser:83)
INFO Kafka commitId : cb8625948210849f (org.apache.kafka.common.utils.AppInfoParser:84)
INFO task [0_0] Created state store manager for task 0_0 with the acquired state dir lock (org.apache.kafka.streams.processor.internals.ProcessorStateManager:122)
INFO task [0_0] Register global stores [] (org.apache.kafka.streams.processor.internals.ProcessorStateManager:352)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Created active task 0_0 with assigned partitions [input-topic-0] (org.apache.kafka.streams.processor.internals.StreamThread:1248)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating active task 0_1 with assigned partitions [[input-topic-1]] (org.apache.kafka.streams.processor.internals.StreamThread:1229)
INFO task [0_1] Created state store manager for task 0_1 with the acquired state dir lock (org.apache.kafka.streams.processor.internals.ProcessorStateManager:122)
INFO task [0_1] Register global stores [] (org.apache.kafka.streams.processor.internals.ProcessorStateManager:352)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Created active task 0_1 with assigned partitions [input-topic-1] (org.apache.kafka.streams.processor.internals.StreamThread:1248)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating active task 1_0 with assigned partitions [[stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-0]] (org.apache.kafka.streams.processor.internals.StreamThread:1229)
INFO task [1_0] Created state store manager for task 1_0 with the acquired state dir lock (org.apache.kafka.streams.processor.internals.ProcessorStateManager:122)
INFO task [1_0] Register global stores [] (org.apache.kafka.streams.processor.internals.ProcessorStateManager:352)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Created active task 1_0 with assigned partitions [stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-0] (org.apache.kafka.streams.processor.internals.StreamThread:1248)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Creating active task 1_1 with assigned partitions [[stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-1]] (org.apache.kafka.streams.processor.internals.StreamThread:1229)
INFO task [1_1] Created state store manager for task 1_1 with the acquired state dir lock (org.apache.kafka.streams.processor.internals.ProcessorStateManager:122)
INFO task [1_1] Register global stores [] (org.apache.kafka.streams.processor.internals.ProcessorStateManager:352)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Created active task 1_1 with assigned partitions [stream-app-KSTREAM-AGGREGATE-STATE-STORE-0000000004-repartition-1] (org.apache.kafka.streams.processor.internals.StreamThread:1248)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Starting restoring state stores from changelog topics [] (org.apache.kafka.streams.processor.internals.StoreChangelogReader:121)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] Adding assigned standby tasks {} (org.apache.kafka.streams.processor.internals.StreamThread:1345)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] State transition from ASSIGNING_PARTITIONS to RUNNING. (org.apache.kafka.streams.processor.internals.StreamThread:980)
INFO stream-client [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649] State transition from REBALANCING to RUNNING. (org.apache.kafka.streams.KafkaStreams:229)
INFO stream-thread [stream-app-d85064bc-a649-43bd-b74a-ba6fc5f51649-StreamThread-1] partition assignment took 147 ms.
	current active tasks: [0_0, 0_1, 1_0, 1_1]
	current standby tasks: [] (org.apache.kafka.streams.processor.internals.StreamThread:193)

Con esto podemos ver que nuestra aplicación se construyó de forma correcta y que está lista para desplegarla en nuestro cluster.

Si quieres ver el código completo puedes verlo en el siguiente enlace https://github.com/raidentrance/kafka-streams-example.

En este ejemplo vimos algo básico de Kafka streams en futuros posts veremos ejemplos cada vez más complejos, síguenos en nuestras redes sociales para enterarte sobre nuevo contenido https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Kafka streams: Primera aplicación con Kafka streams


El primer ejemplo que escribiremos será una topología que leerá mensajes de un topic de Kafka y contará el número de palabras en el mismo emitiendo el resultado a un topic de salida, para hacerlo se realizarán las siguientes operaciones:

  • Lee un mensaje de kafka
  • Transforma el mensaje en minúsculas
  • Separa el mensaje por espacios en un arreglo de Strings
  • Selecciona la palabra como una llave
  • Agrupa los mensajes por el nombre de la llave
  • Emite el resultado a un topic de salida

Para hacer lo anterior seguiremos los siguientes pasos:

1 Iniciar el cluster de Zookeeper y Kafka

El primer paso será iniciar nuestro cluster de Kafka con los siguientes comandos:

Zookeeper

sh zookeeper-server-start.sh ../config/zookeeper.properties

Kafka

sh kafka-server-start.sh ../config/server.properties

Con lo anterior tendremos un cluster de Kafka y Zookeeper corriendo en nuestra máquina.

2 Crear los topics de Kafka a utilizar

En este ejemplo utilizaremos 2 topics, un topic de entrada y uno de salida, así que el siguiente paso es crearlos, para hacerlo ejecutaremos los siguientes comandos:

sh kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 2 --topic input-topic
sh kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 2 --topic output-topic

Con lo anterior contaremos con dos topics input-topic y output-topic cada uno con 1 replica y 2 particiones. Si deseas confirmar que los topics se crearon correctamente, puedes ejecutar el siguiente comando para listar los topics:

sh kafka-topics.sh --list --zookeeper localhost:2181

Esto te mostrará los topics que existen en tu cluster.

Paso 3 Configuración del proyecto

Una vez que ya iniciamos nuestro cluster y creamos nuestros topics, el siguiente paso será crear nuestro proyecto de Java que leerá de un topic, hará modificaciones a la información y colocará el resultado en un topic de salida.

Para esto agregaremos las siguientes dependencias a nuestro proyecto:

pom.xml

Paso 4 Programando nuestra topología

Una vez que ya tenemos configurado nuestro proyecto, el siguiente paso será programar nuestra topología, veamos el siguiente código:


import java.util.Arrays;
import java.util.Properties;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.KafkaStreams;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.KStreamBuilder;
import org.apache.kafka.streams.kstream.KTable;

public class StreammingApplication {

	public static Properties getKafkaConfiguration() {
		Properties props = new Properties();
		props.put(StreamsConfig.APPLICATION_ID_CONFIG, "stream-app");
		props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
		props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
		props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
		props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
		return props;
	}

	public static void main(String[] args) {
		KStreamBuilder builder = new KStreamBuilder();

		KStream wordCountsInput = builder.stream("input-topic");
		KTable counts = wordCountsInput.mapValues(String::toLowerCase)
				.flatMapValues(value -> Arrays.asList(value.split(" "))).selectKey((ignoredKey, word) -> word)
				.groupByKey().count();

		counts.to(Serdes.String(), Serdes.Long(), "output-topic");

		KafkaStreams streams = new KafkaStreams(builder, getKafkaConfiguration());
		streams.start();
		Runtime.getRuntime().addShutdownHook(new Thread(streams::close));
	}
}

En el código anterior podemos ver lo siguiente:

  • Hay un método que devuelve la configuración de Kafka a utilizar
  • Creamos un objeto de tipo KStreamBuilder que nos permitirá definir nuestro topología de Kafka streams.
  • KStreamBuilder.stream() devuelve un flujo de entrada, el cual es básicamente un consumer, a una referencia de tipo KStream<String, String> contiene 2 genéricos porque uno será para la llave del mensaje y el otro para el valor.
  • KStream nos permitirá realizar las siguientes operaciones:
    • mapValues(..) : Lo utilizaremos para transformar nuestro mensaje a minúsculas.
    • flatMapValues(..): Lo utilizaremos para separar nuestro mensaje en multiples mensajes, uno por cada palabra.

    • selectKey(..) : Nos permitirá seleccionar una llave, lo utilizaremos para utilizar la palabra como llave de nuestro mensaje.

    • groupByKey() : Nos permitirá agrupar por una llave, en este caso será la palabra.

    • count(): Nos permitirá el número de mensajes.
  • Lo anterior nos devolverá un objeto de tipo KTable<String,Long> como respuesta en el primer genérico tendremos las palabras y en el segundo el conteo.
  • Utilizaremos counts.to(Serdes.String(), Serdes.Long(), “output-topic”); para emitir la respuesta a otro topic llamado “output-topic“, utilizamos Serdes.String(), Serdes.Long() para definir el topi de dato de la llave y el valor del mensaje de salida.

Paso 5 probando nuestra aplicación

Una vez que ya entendimos el código el siguiente paso será probarlo, para esto abriremos 2 consumers y un producer del siguiente modo:

  • Consumer 1 : Leerá los mensajes que se envíen al topic “input-topic“.
  • Consumer 2: Leerá los mensajes que se envíen al topic “output-topic
  • Producer 1: Enviará mensajes al topic “input-topic

Para hacerlo utilizaremos los siguientes comandos en diferentes terminales:

Consumer1

 sh kafka-console-consumer.sh --zookeeper localhost:2181  --topic input-topic

Consumer2

 sh kafka-console-consumer.sh --zookeeper localhost:2181  --topic output-topic --formatter kafka.tools.DefaultMessageFormatter --property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer --property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer --property print.key=true

Producer1

sh kafka-console-producer.sh --broker-list localhost:9092 --topic input-topic

Como podemos ver el consumer 2 es un poco más complejo que el uno ya que especificaremos serializers y deserializers diferentes a los que están por defecto, más adelante hablaremos sobre esto.

Una vez que ya contamos con nuestros producers y consumers listos tendremos que ejecutar nuestra aplicación como cualquier otra aplicación java, una vez hecho esto el último paso será escribir mensajes y presionar enter en la consola donde tenemos el producer, en nuestro caso escribiremos el mensaje:

devs4j es el mejor sitio web de programación lo buscaré en Facebook y Twitter y los seguiré

Una vez hecho esto, veremos en el Consumer1 lo siguiente:

devs4j es el mejor sitio web de programación lo buscaré en Facebook y Twitter y los seguiré

Este es el mensaje tal cuál que enviamos, lo interesante viene cuando vemos el Consumer2:

devs4j	1
es	3
sitio	1
web	1
y	2
seguiré	1
el	1
mejor	1
de	1
programación	1
lo	1
buscaré	1
en	1
facebook	1
twitter	1
los	1

Como vemos la frase se separó en diferentes mensajes cada uno con su propio número de ocurrencias.

Como vemos Kafka streams es muy fácil de utilizar y es posible realizar transformación a grandes flujos de información en tiempo real.

Si quieres ver el código completo puedes verlo en el siguiente enlace https://github.com/raidentrance/kafka-streams-example.

En este ejemplo vimos algo básico de Kafka streams en futuros posts veremos ejemplos cada vez más complejos, síguenos en nuestras redes sociales para enterarte sobre nuevo contenido https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Kafka Streams : Introducción


Kafka Streams es una biblioteca utilizada para el procesamiento y transformación de datos utilizando Apache Kafka. Es posible utilizarlo para múltiples casos, veamos algunos ejemplos:

  • Transformación de datos
  • Detección de patrones
  • Detección de fraudes
  • Monitoreo y alertas
  • Migración de datos

Para utilizar Kafka streams solo programaremos una aplicación común Java, no es necesario crear un cluster separado a diferencia de Spark, Storm, Apache Flink, etc. Al utilizar Kafka tendrá todos sus beneficios y el procesamiento será de uno a uno, esto significa que no procesaremos multiples mensajes al mismo tiempo.

Arquitectura de Kafka

Kafka streams es un jar que se instala en un cluster Kafka, este leerá información desde y hacia kafka como se muestra en la siguiente imagen:

kafka streams arq

Para dar los primeros pasos con Apache Kafka les recomendamos el siguiente post Primeros pasos con Apache Kafka en Español !.

Terminología

A continuación se muestra la terminología a utilizar a lo largo de los posts:

  • Stream : Es una secuencia de registros inmutable, ordenado y tolerante a fallas.
  • Stream processor: Es un nodo de procesamiento.
  • Topology: Una topología es un grafo compuesto por streams y stream processors

streams-processors-topology

  • Source processor: Es un processor especial que lee datos directamente de un kafka topic, no hay nadie antes de el y no hace modificaciones a los datos.
  •  Sink processor:Es un processor que no tiene hijos, manda un stream directamente a un Kafka topic.

En este post vimos los primeros pasos y la terminología de Kafka streams, en siguientes posts explicaremos en detalle su funcionamiento, síguenos en nuestras redes sociales para enterarte sobre nuevo contenido https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

 

 

Conecta una aplicación a multiples bases de datos condicionalmente


Un problema común en aplicaciones complejas, es cuando deseas desarrollar una aplicación que consume multiples bases de datos de acuerdo al país, area, cliente, etc. Una decisión común para resolver este problema es crear diferentes repositorios de código para cada uno, el problema es al momento de mantener el código y actualizaciones al sistema.

En este post explicaremos como podemos contar con un solo código que se pueda conectar a diferentes bases de datos de acuerdo a una configuración.

Paso 1 Creando bases de datos

El primer paso será crear dos bases de datos, una será utilizada para almacenar usuarios de Estados unidos y la otra para México:

create database us_users;
use us_users;
CREATE TABLE USER(
USER_ID INTEGER PRIMARY KEY AUTO_INCREMENT,
USERNAME VARCHAR(100) NOT NULL,
PASSWORD VARCHAR(100) NOT NULL
);
INSERT INTO USER (USERNAME,PASSWORD)VALUES('emma','superSecret');
INSERT INTO USER (USERNAME,PASSWORD)VALUES('john','smith');
INSERT INTO USER (USERNAME,PASSWORD)VALUES('kc','helloworld');

Lo anterior creará una tabla de usuarios para Estados Unidos junto con algunos registros de ejemplo.

create database mx_users;
use mx_users;
CREATE TABLE USER(
USER_ID INTEGER PRIMARY KEY AUTO_INCREMENT,
USERNAME VARCHAR(100) NOT NULL,
PASSWORD VARCHAR(100) NOT NULL
);
INSERT INTO USER (USERNAME,PASSWORD)VALUES('Alejandro','superSecreta');
INSERT INTO USER (USERNAME,PASSWORD)VALUES('Pedro','Pablito');
INSERT INTO USER (USERNAME,PASSWORD)VALUES('Pancho','Pantera');

Lo anterior creará una tabla de usuarios para México junto con algunos registros de ejemplo.

Paso 2 Configurado el proyecto

El proyecto se creará con Spring boot con las siguientes dependencias:

https://github.com/raidentrance/spring-multiple-db/blob/master/pom.xml

Con lo anterior tendremos todo lo necesario para trabajar con spring-mvc y con spring-jdbc.

Paso 3 Creando la clase aplicación

Una vez que tenemos configurado spring boot tendremos que crear la clase aplicación, que será quien inicie nuestra api.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

/**
 * @author raidentrance
 *
 */
@SpringBootApplication
public class SampleApplication extends SpringBootServletInitializer{
	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(SampleApplication.class);
	}

	public static void main(String[] args) {
		SpringApplication.run(SampleApplication.class, args);
	}

}

Para ejecutar nuestra aplicación solo ejecutaremos la clase anterior y los servicios se expondrán de forma exitosa.

Paso 4 Configurando datasources

El siguiente paso será configurar nuestros datasources, para esto crearemos un paquete llamado com.devs4j.example.config.db con la siguiente clase:


import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author raidentrance
 *
 */
@Configuration
public class DataSourceConfig {
	@Bean
	@ConditionalOnProperty(name = "market", havingValue = "mx")
	public DataSource mxDatasource() {
		return DataSourceBuilder.create().driverClassName("com.mysql.jdbc.Driver")
				.url("jdbc:mysql://localhost:3306/mx_users").username("root").password("root").build();

	}

	@Bean
	@ConditionalOnProperty(name = "market", havingValue = "us")
	public DataSource usDatasource() {
		return DataSourceBuilder.create().driverClassName("com.mysql.jdbc.Driver")
				.url("jdbc:mysql://localhost:3306/us_users").username("root").password("root").build();
	}

}

Como se puede ver se crearon 2 datasources cada uno apuntando a una base de datos diferente, como se puede ver se hace uso de @ConditionalOnProperty, esto significa que creará el bean siempre y cuando se cumpla con la condición que define, que en este caso es que el valor de la propiedad market sea igual a us o mx. Otro punto importante es que solo un bean se creará, en caso contrario Spring no sabría que bean inyectar.

Paso 5 Creando el modelo de nuestra aplicación

Una vez que creamos la tabla, el siguiente paso será crear una clase que la represente, para esto crearemos la siguiente clase:


/**
 * @author raidentrance
 *
 */
public class User {
	private Integer id;
	private String username;
	private String password;

	public User() {
	}

	public User(Integer id, String username, String password) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

Se creará un objeto de la clase User por cada registro que se desee devolver.

Paso 6 Creando un DAO de spring jdbc

El siguiente paso será hacer uso de Spring jdbc para ejecutar una consulta a nuestra base de datos, para esto crearemos el siguiente DAO (Data access object):


import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import com.devs4j.example.model.User;

/**
 * @author raidentrance
 *
 */
@Component
public class UserDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;

	private static final String GET_ALL = "select * from user";

	public List getUsers() {
		return jdbcTemplate.query(GET_ALL, new RowMapper() {

			@Override
			public User mapRow(ResultSet rs, int rowNum) throws SQLException {
				return new User(rs.getInt(1), rs.getString(2), rs.getString(3));
			}
		});
	}
}

Nuestro DAO solo cuenta con un método que devuelve todos los usuarios en la tabla.

Paso 7 Creando un Service de spring

En este ejemplo no es tan necesario, pero lo crearemos para que quede clara la capa de servicios en nuestro servicio:


import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.devs4j.example.dao.UserDao;
import com.devs4j.example.model.User;

/**
 * @author raidentrance
 *
 */
@Service
public class UserService {
	@Autowired
	private UserDao dao;

	public List getUsers() {
		return dao.getUsers();
	}
}

El servicio UserService utilizará al DAO creado previamente para obtener la información de los usuarios a devolver.

Paso 8 Creando el Controller

Una vez hecho lo anterior, el siguiente paso será crear un controller que expondrá la información obtenida vía HTTP a través de un servicio REST:


import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.devs4j.example.model.User;
import com.devs4j.example.service.UserService;

/**
 * @author raidentrance
 *
 */
@RestController
@RequestMapping("/api")
public class UserController {
	@Autowired
	private UserService service;

	@RequestMapping("/users")
	@ResponseBody
	public ResponseEntity getUsers() {
		return new ResponseEntity(service.getUsers(), HttpStatus.OK);
	}
}

Como se puede ver se expondrá un endpoint GET /api/users que devolverá la lista de usuarios.

Paso 9 Creando nuestro archivo application.properties

Por último crearemos un archivo llamado application.properties en el folder /src/main/resources con lo siguiente:

market=mx

Lo anterior definirá el mercado que utilizará la aplicación, en este caso será México.

Paso 10 Probando la aplicación

Una vez que tenemos todo lo anterior, el último paso será probar el api, para esto ejecutaremos nuestra aplicación (recordando que el mercado que definimos fue México) y abriremos la url http://localhost:8080/api/users con la siguiente salida:

[
    {
        "id": 5,
        "username": "Alejandro",
        "password": "superSecreta"
    },
    {
        "id": 6,
        "username": "Pedro",
        "password": "Pablito"
    },
    {
        "id": 7,
        "username": "Pancho",
        "password": "Pantera"
    }
]

Como podemos ver los usuarios que se muestran son los que definimos en la base de datos de México, ahora modificaremos el archivo application.properties con lo siguiente:

market=us

Una vez hecho esto ejecutaremos nuevamente la aplicación e invocaremos de nuevo la url http://localhost:8080/api/users con la siguiente salida:

[
    {
        "id": 1,
        "username": "emma",
        "password": "superSecret"
    },
    {
        "id": 2,
        "username": "john",
        "password": "smith"
    },
    {
        "id": 3,
        "username": "kc",
        "password": "helloworld"
    }
]

Como vemos ahora estamos obteniendo la información de la base de datos de Estados Unidos.

Puedes encontrar el código completo en el siguiente link https://github.com/raidentrance/spring-multiple-db/blob/master/pom.xml.

Si te gusta el contenido y quieres enterarte cuando realicemos un post nuevo síguenos en nuestras redes sociales https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Spring boot : Spring data+ Spring mvc + H2, utilizando bases de datos en memoria


Spring boot se ha convertido en uno de los frameworks más fáciles de utilizar en Java para distintos tipos de aplicaciones, en este post explicaremos la integración de Spring boot con la base de datos H2 para el desarrollo y pruebas de nuestras aplicaciones.

¿Qué es H2?

H2 es una base de datos relacional llamada In memory database (Base de datos en memoria), esto significa que los datos solo vivirán durante la ejecución de nuestra aplicación y cuando esta termine se perderán. El uso de este tipo de bases de datos es muy común para desarrollar pruebas de concepto y realizar pruebas unitarias.

Las bases de datos en memoria son diferentes a las bases de datos normales por lo siguiente, al usar una base de datos común hacemos lo siguiente:

common-db

Como podemos ver  la aplicación y la base de datos son independientes, esto significa que se debe crear la base de datos y mantenerla para poder tener acceso a ella. A diferencia de esto, una base de datos en memoria funcionará del siguiente modo:

in memory-db

Como vemos una vez que iniciemos nuestra aplicación se iniciará nuestra base de datos y esta vivirá durante el tiempo que nuestra aplicación funcione, una vez que la aplicación se detiene los datos se pierden, es por esto que el uso de este tipo de bases de datos es ideal para el desarrollo de pruebas de concepto y tests unitarios dentro de nuestras aplicaciones.

Ventajas

El uso de H2 proporciona las siguientes ventajas:

  • No es necesario invertir en infraestructura
  • No es necesario invertir en configuración
  • No es necesario dar mantenimiento
  • La configuración con Spring boot es super simple

Funcionamiento con Spring boot

Una vez que entendemos el propósito de H2 veamos como funciona con Spring boot.

Paso 1: Configuración

El primer paso será configurar nuestra aplicación Spring boot, veamos las entradas en nuestro archivo pom.xml:


	org.springframework.boot
	spring-boot-starter-parent
	2.0.3.RELEASE

Define el proyecto padre de nuestra aplicación (Configuración común de spring boot).


	
		org.springframework.boot
		spring-boot-starter-data-jpa
	
	
		org.springframework.boot
		spring-boot-starter-web
	
	
		com.h2database
		h2
		runtime
	

Las anteriores son las dependencias necesarias de nuestro proyecto, en este caso solo explicaremos como trabajar con h2 utilizando el api de spring-data.


	
		
			maven-compiler-plugin
			
				1.8
				1.8
			
		
		
			org.springframework.boot
			spring-boot-maven-plugin
		
	

Por último vemos la definición de plugins los cuál nos permitirán ejecutar nuestra aplicación y definir la versión de java a utilizar.

Paso 2 : Definir nuestra clase aplicación

Una vez que definimos nuestras dependencias el siguiente paso será crear nuestra clase Application.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author raidentrance
 *
 */
@SpringBootApplication
@EnableTransactionManagement
@EnableJpaRepositories("com.devs4j.app.repositories")<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
public class H2SampleApplication {
	public static void main(String[] args) {
		SpringApplication.run(H2SampleApplication.class, args);
	}
}

Ejecutaremos esta clase para iniciar nuestra aplicación.

Paso 4: Crear una Entity

En este ejemplo utilizaremos Spring data para ver el funcionamiento de H2, para esto crearemos la siguiente Entity:

@Entity
public class Person {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column(name = "name", nullable = false)
	private String name;

	@Column(name = "nickname", nullable = true)
	private String nickName;

	@Column(name = "age", nullable = false)
	private Integer age;
....Getters and setters
}

La clase anterior define una entidad persona con las siguientes características:

  • Un Id de tipo entero autoincremental
  • Un nombre de tipo String que debe ser not null
  • Un nickName de tipo String que puede ser null
  • Una edad de tipo Integer que debe ser not null

Paso 6 : Crear repository JPA

Como sabemos Spring Data nos permite simplificar el código de acceso a base de datos a través de Repositories, veamos el repositorio a crear:


import org.springframework.data.jpa.repository.JpaRepository;

import com.devs4j.app.entities.Person;

/**
 * @author raidentrance
 *
 */
public interface PersonRepository extends JpaRepository{
}

Como se puede ver PersonRepository nos permitirá manejar entidades de tipo Person, más adelante veremos como utilizaremos esta interfaz en nuestro código.

Paso 7 : Creando el servicio

El siguiente paso será crear un servicio de Spring, estos servicios son diseñados para escribir la lógica de negocio que necesitemos, en este caso no es tan necesario ya que solo ejecutará la llamada a un repository, pero en casos en los que hace llamadas a multiples repositorios y ejecuta alguna lógica sobre los datos, es muy útil.


import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.devs4j.app.entities.Person;
import com.devs4j.app.repositories.PersonRepository;

/**
 * @author raidentrance
 *
 */
@Service
public class PersonService {

	@Autowired
	private PersonRepository repository;

	public List getPeople() {
		return repository.findAll();
	}
}

Como se puede ver a través de la anotación @Autowired inyectamos el repository dentro de nuestro servicio, recordemos que no es necesario escribir la implementación del repository ya que Spring Data lo hace por nosotros, lo se es hermoso :).

Paso 8 : Creando el controller

El último paso para completar nuestra aplicación será crear un Controller para exponer la información, veamos el código a continuación:


import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.devs4j.app.entities.Person;
import com.devs4j.app.services.PersonService;

/**
 * @author raidentrance
 *
 */
@RestController
@RequestMapping("api")
public class PersonController {

	@Autowired
	private PersonService personService;

	@RequestMapping("/people")
	@ResponseBody
	public ResponseEntity<List> getPeople() {
		return new ResponseEntity(personService.getPeople(), HttpStatus.OK);
	}

}

Como vemos nuestro endpoint /api/people devolverá todos los registros que se encuentren en la tabla person.

Paso 9: Ejecutando nuestra aplicación

En este paso ejecutaremos la clase H2SampleApplication.java y llamaremos la url http://localhost:8080/api/people, al hacer esto obtendremos la siguiente salida:

[ ]

Triste ¿no lo creen? no vemos ningún registro porque nuestras tablas en memoria están vacías, veamos algunas formas de llenarlas.

Trabajando con H2

Como comentamos al principio del post, H2 es una base de datos en memoria, lo cual significa que mantendrá nuestros registros mientras nuestra aplicación esté en ejecución, lo primero que haremos será ver como acceder a la base de datos mientras la aplicación esta en ejecución.

Accediendo a la consola de H2

Lo primero que debemos aprender es a utilizar la consola de H2, para esto agregaremos la siguiente línea a nuestro archivo /src/main/resources/application.properties:

spring.h2.console.enabled=true

Esto nos permitirá habilitar la consola de administración de H2 mientras nuestra aplicación se ejecuta. Una vez hecho esto iniciaremos nuestra aplicación y accederemos a la URL http://localhost:8080/h2-console, esto nos mostrará la siguiente vista:

Captura de pantalla 2018-06-29 a las 1.49.32 p.m.

Es importante cambiar la JDBC URL a jdbc:h2:mem:testdb y oprimir el botón connect, una vez hecho esto veremos lo siguiente:

captura-de-pantalla-2018-06-29-a-las-2-03-34-p-m.png

Como pueden ver hay una tabla llamada PERSON, nosotros no creamos esta tabla si no que fue creada de forma automática por H2 y spring boot, desde esta consola podremos ejecutar sentencias sql para agregar valores, modificarlos o realizar consultas durante la ejecución de nuestra aplicación.

Lo anterior nos da flexibilidad para realizar modificaciones en nuestros datos, pero si nuestra aplicación require que se carguen algunos scripts o que se inserten algunos valores de ejemplo tenemos otra forma de hacerlo, a través de los siguientes archivos:

  • schema.sql : Permite ejecutar sentencias DDL antes de ejecutar la aplicación
  • init.sql : Permite realizar sentencias DML una vez que nuestro archivo schema.sql se ejecutó de forma correcta.

Veamos un ejemplo, crearemos el archivo /src/main/resources/data.sql con las siguientes sentencias:

insert into person (id,name,nickname,age)values(1, 'alex','raidentrance',29);
insert into person (id,name,nickname,age)values(2, 'juan','juanito dubalin',80);
insert into person (id,name,nickname,age)values(3, 'pedro','mascara sagrada',29);

Como se puede ver solo son sentencias insert que generarán algunos valores de ejemplo en nuestra base de datos.

A continuación solo debemos detener nuestra aplicación y ejecutarla de nuevo, al hacerlo podremos ejecutar el endpoint http://localhost:8080/api/people y veremos la siguiente salida:

[
    {
        "id": 1,
        "name": "alex",
        "nickName": "raidentrance",
        "age": 29
    },
    {
        "id": 2,
        "name": "juan",
        "nickName": "juanito dubalin",
        "age": 80
    },
    {
        "id": 3,
        "name": "pedro",
        "nickName": "mascara sagrada",
        "age": 29
    }
]

Como ven al momento de ejecutar la aplicación Spring boot detectó que usamos H2 y que existe un archivo llamado data.sql y ejecutó ese archivo en nuestra base de datos en memoria, esto permitió que nuestro servicio contara con datos desde el principio sin necesidad de cargar ningún valor en la base de datos manualmente.

En siguientes posts explicaremos como ejecutar transacciones y manejarlas utilizando H2 o cualquier otra base de datos.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/devs4j y https://www.facebook.com/devs4j/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

 

 

Mongo DB – Instalación


Mongo db es una base de datos NoSQL que se está volviendo cada vez más popular, en este post explicaremos como iniciar un servidor mongo en tu equipo.

Paso 1 Descargar mongo

Entra al sitio https://www.mongodb.com/download-center#community para ver las versiones de Mongodb disponibles, en este caso instalaremos la versión Community.

wget https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-3.6.5.tgz

Wget te permitirá descargar el tar desde la consola en tu directorio local.

Paso 2 Descomprimir el archivo

Una vez que tienes el archivo en tu maquina el siguiente paso es descomprimirlo, para esto ejecutaremos el siguiente comando:

tar -zxvf mongodb-osx-ssl-x86_64-3.6.5.tgz

Esto descomprimirá el archivo en el directorio en el que te encuentres, el nombre de la carpeta será el mismo que el del archivo sin la extensión .tgz.

Paso 3 Re nombrar el folder

Aveces es un poco complicado lidiar con nombres de folders complejos así que lo re nombraremos a uno más sencillo utilizando el siguiente comando:

mv mongodb-osx-ssl-x86_64-3.6.5 mongodb

Con esto tendremos un nombre simple para nuestro directorio.

Paso 4 Agregar mongo a las variables de entorno

El siguiente paso será agregar los comandos de mongo a nuestras variables de entorno, para esto editaremos el archivo ~/.bash_profile agregando las siguientes dos líneas de código:

export MONGO=/mongodb/bin/
PATH=$PATH:$MONGO

Lo anterior hará disponibles los comandos de mongo desde cualquier directorio, lo único que debemos hacer es actualizar utilizando el siguiente comando:

source ~/.bash_profile

Con lo anterior las variables de entorno quedarán configuradas de forma correcta.

Paso 4 Crear un directorio para almacenar los datos

Por default, mongo tratará de almacenar los datos en el directorio /data/db , así que debes dar permiso a tu usuario para escribir sobre ese directorio. Si deseas utilizar otro directorio puedes ejecutar mongo utilizando la bandera –dbpath como se muestra en el siguiente ejemplo:

mongod --dbpath 

Hecho esto el servicio de mongo quedará iniciado y solo tendremos que conectarnos a este.

Paso 5 Conectarse al servidor

Por ultimo tendremos que conectarnos al servidor de mongo, para esto ejecutaremos el siguiente comando:

mongo --host 127.0.0.1:27017

Una vez hecho esto veremos un prompt que indicará que ya estamos conectados a mongo y nuestra instalación estará completada.

En siguientes posts hablaremos sobre las acciones que podemos realizar en el shell de mongo, síguenos en nuestras redes sociales  https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/ para estar al pendiente.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com