sábado, 19 de octubre de 2019

Java 12 web socket Client




Para este ejemplo vamos a usar la librería de java.net.http con la que podemos usar un objeto de webSocket que nos permitirá conectarnos a algún servicio. En este caso vamos a utilizar un websocket para hacer testing.

También ocupamos usar un servidor publico para hacer testing del websocket http://www.websocket.org/echo.html



Lo primero que vamos a crear es una clase nuevo, con el nombre de WebSocketClient. En esta clase tenemos que hacer un import de la libreria java.net.http.WebSocket. Con esto podremos hacer un implements de la interfaz WebSocket.Listener.


public class WebSocketClient implements WebSocket.Listener {

    @Override
    public void onOpen(WebSocket webSocket) {
        webSocket.request(1);
        System.out.println("We are connected to the server");
    }
    
    @Override
    public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
        System.out.println("Close websocket: " + reason);
        webSocket.sendClose(statusCode, reason);
        return new CompletableFuture<Void>();
    }

    @Override
    public void onError(WebSocket webSocket, Throwable error) {
        System.out.println("Exception " + error.getCause());
        System.out.println("Error: " + error.getLocalizedMessage());
        webSocket.abort();
    }

    @Override
    public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
        System.out.println("Data we got from server: " + data);
        return new CompletableFuture().completedFuture("onText completed.").thenAccept(System.out::println);
    }
}

Para que nuestro sistema funcione se tiene que agregar los siguientes paquetes a la clase.


import java.net.http.WebSocket;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;


Con esta interfaz podemos sobrescribir una serie de métodos para poder establecer la comunicación correcta con el servicio.

Para este ejemplo vamos a usar el metodo onOpen para conectarnos al webSocket, onClose para poder cerrar la conexion del webSocket, onError en caso de que alguna cosa ocurra con la conexion que estamos estableciendo con el servidor, onText que nos permite recibir las respuestas del servidor.

Ahora nos vamos hacer una clase nueva para nuestro metodo main para poder ejecutar nuestro sistema para conectarnos al websocket.

Debemos hacer los siguientes imports para poder inicializar nuestro cliente


import java.net.URI;
import java.net.http.HttpClient;
import javax.swing.JOptionPane;

Ahora tenemos que crear un objeto de tipo WebSocketClient que tiene los métodos que esperan los eventos del websocket.


    public static void main(String[] args) {
        var client = new WebSocketClient();
        var serverConection = HttpClient.newHttpClient().newWebSocketBuilder().buildAsync(URI.create("ws://echo.websocket.org"), client);
        var connectedClient = serverConection.join();
        var result = JOptionPane.showInputDialog("Message");
        while (true) {
            connectedClient.sendText(result, true);
            result = JOptionPane.showInputDialog("Message");
        }

    }

Luego creamos otro objeto (httpClient) usando el newHttpClient() usando el método newWebSocketBuilder vamos a crear nuestra conexión. Que tiene un método llamado buildAsync el cual requiere del URL de nuestro websocket y le pasamos el objeto que tiene todos los eventos para usar el websocket.

Con esto ya podemos crear una variable String que recibe un texto del JOptionPane con los mensajes que el usuario le quiere enviar al servidor. Con el método sendText podemos enviar los mensajes que el usuario quiere. La respuesta la vamos a esperar en el método onText.

Etiquetas: , , , ,

miércoles, 9 de octubre de 2019

Bulk Delete Cosmosdb



Para borrar documentos de cosmosdb existen varias formas de hacerlo se puede hacer con el SDK, etc. buscando en los documentos de Microsoft y cosmosdb encontré una forma muy fácil y simple para poder borrar documentos.

Lo primero que tenemos que hacer es crear un StoreProcedure en la collection donde deseamos borrar los documentos.

Damos clic a la colección de datos y luego nos vamos al botón que dice New Store Procedure de esta manera podemos iniciar a agregar el código que se ocupa para borrar los documentos.


Le damos el nombre que deseamos al SP.

Ahora viene la interesante el SP de javascript que vamos a usar para hacer el borrado de documentos no lo escribí yo. Este lo encontré en este documento que además tiene ejemplos para otro tipo de procedimientos almacenados.


function bulkDelete(query) {
     var collection = getContext().getCollection();
     var collectionLink = collection.getSelfLink();
     var response = getContext().getResponse();
     var responseBody = {
         deleted: 0,
         continuation: true
     };
     if (!query) throw new Error("The query is undefined or null.");
     tryQueryAndDelete();
     function tryQueryAndDelete(continuation) {
         var requestOptions = {continuation: continuation};
         var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, retrievedDocs, responseOptions) {
             if (err) throw err;
             if (retrievedDocs.length > 0) {
                 tryDelete(retrievedDocs);
             } else if (responseOptions.continuation) {
                 tryQueryAndDelete(responseOptions.continuation);
             } else {
                 responseBody.continuation = false;
                 response.setBody(responseBody);
             }
         });
         if (!isAccepted) {
             response.setBody(responseBody);
         }
     }
     function tryDelete(documents) {
         if (documents.length > 0) {
             var isAccepted = collection.deleteDocument(documents[0]._self, {}, function (err, responseOptions) {
                 if (err) throw err;
                 responseBody.deleted++;
                 documents.shift();
                 tryDelete(documents);
             });
             if (!isAccepted) {
                 response.setBody(responseBody);
             }
         } else {
             tryQueryAndDelete();
         }
     }
 }

Yo lo probé y funciona muy bien. Lo interesante es que solamente le tenemos que pasar un script tipo SQL para borrar los documentos que necesitamos. Y bueno se pueden hacer todos los cambios que necesite para que se ajuste mejor a sus necesidades.

Etiquetas: , , , ,

viernes, 4 de octubre de 2019

CosmosDB Singleton


Hace unos días me toco trabajar en proyecto grande con cosmosDB. Inicialmente cada petición que tenia se establecía una conexión y al terminar se terminaba la conexión. Con el incremento de peticiones al servicio Rest iniciamos a tener errores con el Socket. La conexión la tenía implementada con TCP.

Por lo que cambie la conexión del API a utilizar un singleton para poder eliminar el problema.

Vamos a ver como podemos hacer el singleton para un proyecto de Rest API con C# y cosmosDB.
Primero vamos hacer un proyecto nuevo de MVC para crear nuestro Rest API.


Con el proyecto listo lo primero que vamos hacer es una carpeta nueva que se llama services. Aquí vamos a guardar las clases y la interfaz que nos permitirá hacer las consultas a nuestra base de datos.


En la interfaz ICosmosDBServices lo que debemos agregar son los métodos que van a realizar las operaciones en cosmosDB. En nuestro ejemplo tenemos métodos para buscar información agregar documentos y borrar, pero podríamos hacer todos los que necesitamos.


namespace CosmosDBSingleton.Serivces
{
    public interface ICosmosDBService
    {
        dynamic FindDocument(string documentid);
        Task<dynamic> CreateNewDocumentAsync();
        Task<bool> ReplaceDocument();
    }
}

La clase CosmosDBServices la vamos a usar para desarrollar los métodos que se definieron en la interfaz. Para nuestro ejemplo solo usaremos en método de buscar. Pero se pueden crear todos.


public class CosmosDBService : ICosmosDBService
    {

        private readonly DocumentClient _client;
        private readonly FeedOptions _queryOptions;

        public CosmosDBService(string endpointUrl, string primaryKey)
        {
            var connectionPolicy = new ConnectionPolicy()
            {
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp,
                EnableEndpointDiscovery = true
            };
            _client = new DocumentClient(new Uri(endpointUrl), primaryKey, connectionPolicy);
            _queryOptions = new FeedOptions { MaxItemCount = 1000, EnableCrossPartitionQuery = true };
        }

En esta clase también vamos a definir un objeto de conexión a la base de datos. Que tiene de nombre _client. El constructor de la clase requiere que se le pase el endpoint de conexión y la llave de seguridad de la base de datos para poder crear esta conexión. Dentro del constructor configuramos alguna información de la conexión y establecemos la conexión a la base de datos.

En este momento ya podemos usar la conexión de la base de datos para consultas borrar o actualizar documentos.

Como vemos el método FindDocument requiere de un parámetro para hacer la consulta y ya con esto podemos iniciar la consulta de documentos a la base de datos.


 public Task<dynamic> CreateNewDocumentAsync()
        {
            throw new NotImplementedException();
        }

        public dynamic FindDocument(string documentid)
        {
            var query = new SqlQuerySpec()
            {
                QueryText = "SELECT top 1 * FROM c where c.id=@id",
                Parameters = new SqlParameterCollection() { new SqlParameter("@id", documentid) }
            };
            var crossQueryInSql = _client.CreateDocumentQuery<dynamic>(UriFactory.CreateDocumentCollectionUri("cosmosdb database", "cosmosdb collection"), query, _queryOptions).ToList();
            return crossQueryInSql.FirstOrDefault();
        }

        public Task<bool> ReplaceDocument()
        {
            throw new NotImplementedException();
        }

        public async Task<bool> OpenConnection()
        {
            await _client.OpenAsync();
            return true;
        }

Ahora nos debemos ir a la clase Startup donde crearemos la conexión a cosmos. Usando la configuración de los appsettings pasamos la información requerida a la clase de cosmosDBServices.


 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddSingleton(InitializeCosmosClientInstanceAsync(_config.GetSection("CosmosDBSettings")));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

services.AddSingleton(InitializeCosmosClientInstanceAsync(_config.GetSection("CosmosDBSettings")));

Dentro de esta clase creamos un método llamado InitializeCosmosClientInstanceAsync el cual recibe la información del appsettings con la información de cosmosdb. Ahora lo que debemos hacer es crear una instancia de CosmosDBService con los settings y retornar el objeto listo con la información de la conexión. De esta manera vamos a tener una conexión única para hacer las operaciones a cosmosdb.


  private static ICosmosDBService InitializeCosmosClientInstanceAsync(IConfigurationSection configurationSection)
        {
            string endpoint = configurationSection.GetSection("CosmosDBAPi").Value;
            string key = configurationSection.GetSection("CosmosDBKey").Value;

            var cosmosDbService = new CosmosDBService(endpoint, key);
            cosmosDbService.OpenConnection().GetAwaiter().GetResult();
            return cosmosDbService;
        }


appsettings


Después de esto los errores con las conexiones y los sockets se terminaron.


Etiquetas: , , , , , ,