Saturday, May 15, 2021

Upload-Download Any FileType .PDF,MP3,MP4,.DOCX in Database and Without Database in SpringBoot Gradle Build System - Java

 Upload-Download Any FileType .PDF,MP3,MP4,.DOCX in Database and Without Database in SpringBoot :

I have asked to create a file upload-download demo in spring boot on the Gradle build system. So this for my students and beginners who want to understand the API how to develop file upload-download. Even you can view the same in your browser.

Our Spring Boot Application will provide APIs for:

Uploading File to MySQL database

downloading File database with the link

These are APIs to be exported:

Methods Urls Actions

POST /upload upload a File

GET /files get List of Files (name, url, type, size)

GET /files/[fileId] download a File

The uploaded files will be stored in the MySQL Database files table with these fields: id, name, type, and data as BLOB type (Binary Large Object is for storing binary data like file, image, audio, or video).

Let me explain it briefly.

– FileDocument is the data model corresponding to the files table in the database.

– FileDBRepository extends the Spring Data JPA repository which has methods to store and retrieve files.

– FilesStorageService uses FileDBRepository to provide methods for saving new file, get file by id, get list of Files.

– FilesController uses FilesStorageService to export Rest APIs: POST a file, GET all files’ information, download a File.

– FileUploadExceptionAdvice handles exceptions when the controller processes file upload.

– ResponseFile contains information of the file (name, URL, type, size) for HTTP response payload.

– application.YAML contains configuration for Servlet Multipart and MySQL database connection.

– pom.xml for Spring Boot, Spring Data JPA, and MySQL connector dependency.

Full Souce Code Accessible Here on below path:
https://github.com/Abhinaw/demo/tree/master

Below is the test results for reference :



Folder Location where file is getting stored : FileStorage Folder in project

     Folder Location where file is getting stored

Source Code :

import com.greenlearner.fileuploaddownload.dto.FileUploadResponse;
import com.greenlearner.fileuploaddownload.service.FileStorageService;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@RestController
public class UploadDownloadWithFileSystemController {

private FileStorageService fileStorageService;

public UploadDownloadWithFileSystemController(FileStorageService fileStorageService) {
this.fileStorageService = fileStorageService;
}

@PostMapping("single/upload")
FileUploadResponse singleFileUplaod(@RequestParam("file") MultipartFile file) {

String fileName = fileStorageService.storeFile(file);

///http://localhost:8081/download/abc.jpg
String url = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/download/")
.path(fileName)
.toUriString();

String contentType = file.getContentType();

FileUploadResponse response = new FileUploadResponse(fileName, contentType, url);

return response;

}

@GetMapping("/download/{fileName}")
ResponseEntity<Resource> downLoadSingleFile(@PathVariable String fileName, HttpServletRequest request) {

Resource resource = fileStorageService.downloadFile(fileName);

// MediaType contentType = MediaType.APPLICATION_PDF;

String mimeType;

try {
mimeType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException e) {
mimeType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
mimeType = mimeType == null ? MediaType.APPLICATION_OCTET_STREAM_VALUE : mimeType;

return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(mimeType))
// .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;fileName="+resource.getFilename())
.header(HttpHeaders.CONTENT_DISPOSITION, "inline;fileName=" + resource.getFilename())
.body(resource);
}

@PostMapping("/multiple/upload")
List<FileUploadResponse> multipleUpload(@RequestParam("files") MultipartFile[] files) {

if (files.length > 7) {
throw new RuntimeException("too many files");
}
List<FileUploadResponse> uploadResponseList = new ArrayList<>();
Arrays.asList(files)
.stream()
.forEach(file -> {
String fileName = fileStorageService.storeFile(file);

///http://localhost:8081/download/abc.jpg
String url = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/download/")
.path(fileName)
.toUriString();

String contentType = file.getContentType();

FileUploadResponse response = new FileUploadResponse(fileName, contentType, url);
uploadResponseList.add(response);
});

return uploadResponseList;
}

@GetMapping("zipDownload")
void zipDownload(@RequestParam("fileName") String[] files, HttpServletResponse response) throws IOException {
//zipoutstream - zipentry+zipentry

try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
Arrays.asList(files)
.stream()
.forEach(file -> {
Resource resource = fileStorageService.downloadFile(file);

ZipEntry zipEntry = new ZipEntry(resource.getFilename());

try {
zipEntry.setSize(resource.contentLength());
zos.putNextEntry(zipEntry);

StreamUtils.copy(resource.getInputStream(), zos);

zos.closeEntry();
} catch (IOException e) {
System.out.println("some exception while ziping");
}
});
zos.finish();

}

response.setStatus(200);
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;fileName=zipfile");
}
}

This is without a database just upload and download files to a particular location.

application.yaml:

server:
port: 8081

file:
storage:
location: fileStorage

spring:
servlet:
multipart:
enabled: true
location: temp123
file-size-threshold: 5MB
max-file-size: 20MB
max-request-size: 20MB

datasource:
url: jdbc:mysql://localhost:3306/db?useSSL=false
username: root
password:
driver-class-name: com.mysql.cj.jdbc.Driver



Gradle dependency:

dependencies {
//my sql connector
compile 'mysql:mysql-connector-java:8.0.17'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}

Thanks.


Sunday, May 9, 2021

Ktor Kotlin Backend Development with MySql Database Demo Project - Mobile Development

Kotlin-Ktor-MySql-Webservice Development

GitHub Full Source Code:
https://github.com/Abhinaw/nearbyapi_abhi/tree/master

I was getting demand for Ktor Kotlin Backend Development with Sample MySql Database Code So in this blog I will give you all full access to the source code on my Github URL.

SQL file is also committed to the parent folder and you can easily import and configure the project.

Why Ktor?

Use what you need. Ktor allows you to transparently configure only the functionality your project requires. No magic involved!

Extensible

Extend what you need. With a configurable pipeline, you can create the extensions you need and place them anywhere you want.

Multiplatform

Run it where you need it. Built from the ground up with Kotlin Multiplatform technology, you can deploy Ktor applications anywhere.

Asynchronous

Scales as you need it. Using Kotlin coroutines, Ktor is truly asynchronous and highly scalable. Use the power of non-blocking development without the callback nightmare.

Java Threads Vs Kotlin Coroutines 

I myself as a programmer/developer I tested it by writing code that's what I do. I read somewhere that Courotines is much better than Java threads I did not believe it initially but to test it I did one thing like I have an 8GB RAM Multi-core Windows operating system. 

To test it, I wrote a simple Java and Kotlin Program where what i did is like created 100 threads from Fork/Join Pool of threads and the same concept with Kotlin Coroutines and the result was amazing to me. 

In the case of Java, my system got crashed and it consumes my all resources when I checked my task manager it was consuming 100% of my system but in the case of Coroutines with the same program, it only consumed my 80% resource and that is an amazing part to me and even the program did not crash. this makes me a little uncomfortable worked on Java for more than 10 years till now and maybe it proves how powerful Kotlin is. As a developer/programmer I believe it because I checked it by doing my hands dirty with it.

Thoughts for Food for Android Developers:

I strongly believe that it will take time to improve like Spring Eco-System but I find it very useful in terms of Mobile Application Development because in every mobile app we need middle-tier for data transaction and there it saves like earlier for that Mobile Developer can not cater that requirement but now they can easily cater the same because they can write the required code for Middle-Tier certainly Android Developers.

And there is one more advantage that I can see is like you can create a single project structure to care both the platforms I mean Front-End and Middle-Tier, Yes there can be an architectural difference but indeed very help full when it comes to CI/CD.

Ktor is a framework to easily build connected applications – web applications, HTTP services, mobile and browser applications. Modern connected applications need to be asynchronous to provide the best experience to users, and Kotlin coroutines provide awesome facilities to do it in an easy and straightforward way.

While not yet entirely there, the goal of Ktor is to provide an end-to-end multiplatform application framework for connected applications. Currently, JVM client and server scenarios are supported, as well as JavaScript, iOS, and Android clients, and we are working on bringing server facilities to native environments, and client facilities to other native targets.

When you create a project in IntelliJ IDE:

Name: Specify a project name.

Location: Specify a directory for your project.

Build System: Choose the desired build system. This can be Gradle with Kotlin or Groovy DSL or Maven.

Website: Specify a domain used to generate a package name.

Artifact: This field shows a generated artifact name.

Ktor Version: Choose the required Ktor version.

Engine: Select an engine used to run a server.

Configuration in: Choose whether to specify server parameters in code or in a HOCON file.

On the next page, you can choose a set of features- building blocks that provide common functionality of a Ktor application, for example, authentication, serialization, and content-encoding, compression, cookie support, and so on.

application.conf:

ktor {

    deployment {

        port = 8080

    }

    application {

        modules = [ com.jetbrains.handson.chat.server.ApplicationKt.module ]

    }

}

Application.Kt:

package com.abhinearby

import com.fasterxml.jackson.databind.SerializationFeature

import io.ktor.application.*

import io.ktor.features.*

import io.ktor.gson.*

import io.ktor.http.*

import io.ktor.http.cio.websocket.*

import io.ktor.jackson.*

import io.ktor.request.*

import io.ktor.response.*

import io.ktor.routing.*

import io.ktor.websocket.*

import org.slf4j.event.Level

import java.util.*

import kotlin.collections.LinkedHashSet

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

@Suppress("unused") // Referenced in application.conf

@kotlin.jvm.JvmOverloads

fun Application.module(testing: Boolean = false) {

    install(WebSockets)

    install(CallLogging) {

        level = Level.INFO

        filter { call -> call.request.path().startsWith("/") }

    }

    install(ContentNegotiation) {

        gson {

            setPrettyPrinting()

        }

        jackson {

            enable(SerializationFeature.INDENT_OUTPUT)

        }

    }

 routing {

        /* For only Testing*/

        get("/json/jackson") {

            call.respond(mapOf("hello" to "world"))

        }

        /********************Near By App Start*********************/

        val repository: RegistrationRepository = MySqlRegistrationToDoRepository()

        post("/registration")

        {

            val todoDraft = call.receive<RegistrationDraft>()

            val todo = repository.addToRegistrationTable(todoDraft)

            call.respond(todo)

        }

        get("/getallregistrationdata")

        {

            call.respond(repository.getAllRegisteredTableData())

        }

        put("/registration/{id}")

        {

            val todoDraft = call.receive<RegistrationDraft>()

            val todoId = call.parameters["id"]?.toIntOrNull()

            if (todoId == null) {

                call.respond(HttpStatusCode.BadRequest, "id  parameter has to be a number!")

                return@put

            }

            val update = repository.updateToRegistrationTable(todoId, todoDraft)

            if (update) {

                call.respond(HttpStatusCode.OK)

            } else {

                call.respond(HttpStatusCode.NotFound, "registered user with the id $todoId not found")

            }

        }

        /**************Login********************/

        val loginRepo: LoginRepository = MySqlLoginToDoRepository()

        post("/login")

        {

            val todoDraft = call.receive<LoginDraft>()

            val todo = loginRepo.validateToLoginTable(todoDraft)

            call.respond(todo)

        }

        get("/getlogindata")

        {

            val repository: LoginRepository = MySqlLoginToDoRepository()

            call.respond(repository.getAllLoginTableData())

        }

        ///////////////////////////Websocket Chat System////////////////////////////

        val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())

        webSocket("/chat") {

            println("Adding user!")

            val thisConnection = Connection(this)

            connections += thisConnection

            try {

                send("You are connected! There are ${connections.count()} users here.")

                for (frame in incoming) {

                    frame as? Frame.Text ?: continue

                    val receivedText = frame.readText()

                    val textWithUsername = "[${thisConnection.name}]: $receivedText"

                    connections.forEach {

                        it.session.send(textWithUsername)

                    }

                }

            } catch (e: Exception) {

                println(e.localizedMessage)

            } finally {

                println("Removing $thisConnection!")

                connections -= thisConnection

            }

        }

        /////////////////////////////////////Chat////////////////////////////

    }

}

DatanbaseManager.Kt:


package com.abhinearby.database

import org.ktorm.database.Database

import org.ktorm.dsl.eq

import org.ktorm.dsl.insertAndGenerateKey

import org.ktorm.dsl.update

import org.ktorm.entity.sequenceOf

import org.ktorm.entity.toList

import java.sql.Connection

class DatabaseManager {

private val hostname = "127.0.0.1"

    private val databaseName = "nearby_abhi"

    private val username = "root"

    private val password = ""

    private val ktormDatabase: Database

    init {

        val jdbcUrl = "jdbc:mysql://$hostname:3306/$databaseName?user=$username&password=$password&useSSL=false"

        ktormDatabase = Database.connect(jdbcUrl)

    }

    /************Registration for the App*************/

    fun addRegistrationDataToDB(draft: RegistrationDraft): Registration {

        val insertedId = ktormDatabase.insertAndGenerateKey(DBRegistrationTable)

        {

            set(DBRegistrationTable.firstname, draft.firstname)

            set(DBRegistrationTable.lastname, draft.lastname)

            set(DBRegistrationTable.phoneno, draft.phoneno)

            set(DBRegistrationTable.address, draft.address)

            set(DBRegistrationTable.photo, draft.photo)

            set(DBRegistrationTable.username, draft.username)

            set(DBRegistrationTable.password, draft.password)

        } as Int

        val registration = Registration(

            insertedId,

            draft.firstname,

            draft.lastname,

            draft.phoneno,

            draft.address,

            draft.photo,

            draft.username,

            draft.password

        )

        insertRow(registration)

        return registration

    }

    private fun insertRow(registration: Registration) {

        val id = registration.id

        val username = registration.username

        val password = registration.password

        val fid = registration.id

        val sql =

            "INSERT INTO `login`(`id`, `username`, `password`, `fid`) VALUES ('$id','$username','$password','$fid')"

        ktormDatabase.useConnection { connection: Connection ->

            with(connection) {

                createStatement().execute(sql)

            }

            println("inserted!!")

        }

    }

    fun updateRegistrationDataToDB(id: Int, draft: RegistrationDraft): Boolean {

        val updatedRows = ktormDatabase.update(DBRegistrationTable)

        {

            set(DBRegistrationTable.firstname, draft.firstname)

            set(DBRegistrationTable.lastname, draft.lastname)

            set(DBRegistrationTable.phoneno, draft.phoneno)

            set(DBRegistrationTable.address, draft.address)

            set(DBRegistrationTable.photo, draft.photo)

            set(DBRegistrationTable.username, draft.username)

            set(DBRegistrationTable.password, draft.password)

            where {

                it.id eq id

            }

        }

        return updatedRows > 0

    }

    fun getAllRegisteredTableData(): List<DBNearByRegistrationEntity> {

        return ktormDatabase.sequenceOf(DBRegistrationTable).toList()

    }

    /**************************Registration End*************************************/

    /************************Login **************************/

    fun validateLogin(draft: LoginDraft): LoginDraft {

        val table = "login"

        val sql = "SELECT * FROM $table"

        ktormDatabase.useConnection { connection: Connection ->

            val rs = connection.createStatement().executeQuery(sql)

            while (rs.next()) {

                println(

                    "id: ${rs.getInt("id")}\t" +

                            "username: $${rs.getString("username")}\t" +

                            "password: ${rs.getString("password")}\t" +

                            "fid: $${rs.getDouble("fid")}"

                )

                val userName = rs.getString("username")

                val pass = rs.getString("password")

                if (draft.username.equals(userName)

                    && draft.password.equals(pass)

                ) {

                    break

                }

            }

        }

        return draft!!

    }

    fun getAllLoginTableData(): List<DBNearByLoginEntity> {

        return ktormDatabase.sequenceOf(DBLoginTable).toList()

    }

}

Entity.kt:

import org.ktorm.entity.Entity

import org.ktorm.schema.Table

import org.ktorm.schema.int

import org.ktorm.schema.varchar

object DBRegistrationTable : Table<DBNearByRegistrationEntity>("dbregistrationtable") {

    val id = int("id").primaryKey().bindTo { it.id }

    val firstname = varchar("firstname").bindTo { it.firstname }

    val lastname = varchar("lastname").bindTo { it.lastname }

    var phoneno = varchar("phoneno").bindTo { it.phoneno }

    var address = varchar("address").bindTo { it.address }

    var photo = varchar("photo").bindTo { it.photo }

    var username = varchar("username").bindTo { it.username }

    var password = varchar("password").bindTo { it.password }

}

interface DBNearByRegistrationEntity : Entity<DBNearByRegistrationEntity> {

    companion object : Entity.Factory<DBNearByRegistrationEntity>()

    val id: Int

    val firstname: String

    val lastname: String

    val phoneno: String

    val address: String

    val photo: String

    val username : String

    val password : String

}

RegistrationRepository.kt:

import com.abhinearby.entities.registration.Registration

import com.abhinearby.entities.registration.RegistrationDraft

interface RegistrationRepository {

    fun addToRegistrationTable(draft: RegistrationDraft): Registration

    fun updateToRegistrationTable(id: Int, draft: RegistrationDraft): Boolean

    fun getAllRegisteredTableData() : List<Registration>

}

PostMan Webservcie Request-Response Example:

1)http://localhost:8081/getallregistrationdata

[
    {
        "id": 29,
        "firstname": "abhi",
        "lastname": "tripathi",
        "phoneno": "8130752107",
        "address": "siddha happyville",
        "photo": "",
        "username": "abhi",
        "password": "abhi"
    },
    {
        "id": 30,
        "firstname": "abhinawtripathi34@gmail.com",
        "lastname": "tripathi",
        "phoneno": "8130752107",
        "address": "cyfydufffi",
        "photo": "",
        "username": "test",
        "password": "test"
    },
    {
        "id": 31,
        "firstname": "abhinawtripathi34@gmail.com",
        "lastname": "tripathi",
        "phoneno": "8130752107",
        "address": "cyfydufffi",
        "photo": "",
        "username": "test",
        "password": "test"
    },
    {
        "id": 32,
        "firstname": "buvu",
        "lastname": "cyxx",
        "phoneno": "753577789",
        "address": "vyuvvjvj",
        "photo": "",
        "username": "test",
        "password": "test"
    },
    {
        "id": 33,
        "firstname": "g7g7",
        "lastname": "cyyc",
        "phoneno": "8765677999",
        "address": "gyuj",
        "photo": "",
        "username": "tests107@mailsac.com",
        "password": "test"
    },
    {
        "id": 34,
        "firstname": "g7g7",
        "lastname": "cyyc",
        "phoneno": "8765677999",
        "address": "gyuj",
        "photo": "",
        "username": "tests107@mailsac.com",
        "password": "test"
    },
    {
        "id": 35,
        "firstname": "g7g7",
        "lastname": "cyyc",
        "phoneno": "8765677999",
        "address": "gyuj",
        "photo": "",
        "username": "tests107@mailsac.com",
        "password": "test"
    },
    {
        "id": 36,
        "firstname": "g7g7",
        "lastname": "cyyc",
        "phoneno": "8765677999",
        "address": "gyuj",
        "photo": "",
        "username": "tests107@mailsac.com",
        "password": "test"
    },
    {
        "id": 37,
        "firstname": "test",
        "lastname": "test",
        "phoneno": "875678887",
        "address": "hhjbjhhj",
        "photo": "",
        "username": "test",
        "password": "test"
    },
    {
        "id": 38,
        "firstname": "test",
        "lastname": "test",
        "phoneno": "875678887",
        "address": "hhjbjhhj",
        "photo": "",
        "username": "test",
        "password": "test"
    },
    {
        "id": 39,
        "firstname": "test",
        "lastname": "test",
        "phoneno": "875678887",
        "address": "hhjbjhhj",
        "photo": "",
        "username": "test",
        "password": "test"
    },
    {
        "id": 40,
        "firstname": "",
        "lastname": "",
        "phoneno": "",
        "address": "",
        "photo": "",
        "username": "",
        "password": ""
    },
    {
        "id": 41,
        "firstname": "",
        "lastname": "",
        "phoneno": "",
        "address": "",
        "photo": "",
        "username": "",
        "password": ""
    },
    {
        "id": 42,
        "firstname": "",
        "lastname": "",
        "phoneno": "",
        "address": "",
        "photo": "",
        "username": "",
        "password": ""
    },
    {
        "id": 43,
        "firstname": "",
        "lastname": "",
        "phoneno": "",
        "address": "",
        "photo": "",
        "username": "",
        "password": ""
    },
    {
        "id": 44,
        "firstname": "",
        "lastname": "",
        "phoneno": "",
        "address": "",
        "photo": "",
        "username": "",
        "password": ""
    },
    {
        "id": 45,
        "firstname": "",
        "lastname": "",
        "phoneno": "",
        "address": "",
        "photo": "",
        "username": "",
        "password": ""
    },
    {
        "id": 46,
        "firstname": "abhi",
        "lastname": "sbhi",
        "phoneno": "6858845",
        "address": "ifdjdffuff kgufpfug bufstzcufuc kvfuk. kuddhjcfucucyddcpjvucyf",
        "photo": "",
        "username": "abhi",
        "password": "abhi"
    }
]

2)http://localhost:8081/registration/29 (PUT Servcie)

request:

{
        "id": 29,
        "firstname": "abhinaw",
        "lastname": "tri",
        "phoneno": "8130752107",
        "address": "howrah",
        "photo": "/abc.png",
        "username": "abhinaw",
        "password": "abhinaw"
    }
Response:200OK
Time:251 ms
Size:62 B Save Response





























3)http://localhost:8081/registration (POST Service)









Websocket Communication System:

///////////////////////////Chat////////////////////////////
val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())
webSocket("/chat") {
println("Adding user!")
val thisConnection = Connection(this)
connections += thisConnection
try {
send("You are connected! There are ${connections.count()} users here.")
for (frame in incoming) {
frame as? Frame.Text ?: continue
val receivedText = frame.readText()
val textWithUsername = "[${thisConnection.name}]: $receivedText"
connections.forEach {
it.session.send(textWithUsername)
}
}
} catch (e: Exception) {
println(e.localizedMessage)
} finally {
println("Removing $thisConnection!")
connections -= thisConnection
}
}
/////////////////////////////////////Chat////////////////////////////

Very easy to communicate and very simple code for example.

Output Window:





Saturday, May 8, 2021

Swagger Implementation to Web-services API Spring Boot Project - Java


Spring Boot Web API Swagger Addition to Demo Project:

Note : For full access of code. kindly follow the below link. you can see the working code.

GitHub:

https://github.com/Abhinaw/RestAPiDemoWithDb/tree/master/src/main/java/com/abhi/restapidemodb

application.properties:
For example

server.port=8080
logging.level.org.springframework=info
server.servlet.context-path=/demo
spring.datasource.url=jdbc:mysql://localhost:3306/DB
spring.datasource.username=root
spring.datasource.password=
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=update

Add Swagger Dependencies to POM.XML

<dependency>

    <groupId>io.springfox</groupId>

    <artifactId>springfox-swagger2</artifactId>

    <version>2.9.2</version>

</dependency>

<dependency>

    <groupId>io.springfox</groupId>

    <artifactId>springfox-core</artifactId>

    <version>2.9.2</version>

</dependency>

<dependency>

    <groupId>io.springfox</groupId>

    <artifactId>springfox-swagger-ui</artifactId>

    <version>2.9.2</version>

</dependency>

Create @Configuration File

The next step will be to create a configuration file. This configuration file aims to configure the base package and selectors of your project and make the configured Docket bean available in your application. Once you create this configuration file, there is a lot that will be done by the framework behind the scenes.

Below is an example of a very simple configuration file:

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.PathSelectors;

import springfox.documentation.builders.RequestHandlerSelectors;

import springfox.documentation.spi.DocumentationType;

import springfox.documentation.spring.web.plugins.Docket;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration

@EnableSwagger2

public class SwaggerConfig {

   @Bean

   public Docket apiDocket() {

       Docket docket =  new Docket(DocumentationType.SWAGGER_2)

                .select()

                .apis(RequestHandlerSelectors.basePackage("com.appsdeveloperblog.app.ws"))

                .paths(PathSelectors.any())

                .build();

       return docket;

    } 

}

There are a couple of very important details to note in the above code snippet:

APIs() – here you specify classes that need to be included in Swagger. I provided the base package of my project and thus delegate the job of finding the needed classes to the framework. The base package will be scanned by the framework and needed classes will be included based on the annotations they have,

paths() – here we specify the methods(which are annotated with @Path annotation) from the Controller classes which should be included. Since I want all methods to be included in my documentation created with Swagger, I provide the PathSelectors.any()

Swagger and Spring Security

If your RESTful Web Service application is using Spring Security, you will need to do a little configuration in your Java class, which extends the WebSecurityConfigurerAdapter and annotated with @EnableWebSecurity annotation.

Open the Java class in your Spring Boot project which extends the WebSecurityConfigurerAdapter and which is annotated with @EnableWebSecurity annotation.

Generate Swagger API Documentation JSON

If we run our application now we can get the generated by Swagger JSON documentation of our REST API by opening the following URL in the browser window. I assume you are running your Spring Boot application locally, if so the try this URL:

http://localhost:8080/<CONTEXT PATH HERE>/v2/API-docs








Please note: 

In the URL above you will need to make sure that you provide a correct port number and the Context Path if you have provided one in your application. properties file. If you do not have the context path configured in your application. properties file, then do not provide any context path in the URL above.

For example, if your application properties file has the following entry:

server. servlet.context-path=/demo

server.port=8080

eg: 

http://localhost:8888/demo/v2/api-docs

Swagger UI

API Docs:
localhost:8080/demo/v2/api-docs

For Example:

{"swagger":"2.0","info":{"description":"Api Documentation","version":"1.0","title":"Api Documentation","termsOfService":"urn:tos","contact":{},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0"}},"host":"localhost:8080","basePath":"/demo","tags":[{"name":"user-controller","description":"User Controller"}],"paths":{"/users":{"get":{"tags":["user-controller"],"summary":"list","operationId":"listUsingGET","produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/User"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/users/register":{"post":{"tags":["user-controller"],"summary":"add","operationId":"addUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK"},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/users/{id}":{"get":{"tags":["user-controller"],"summary":"get","operationId":"getUsingGET","produces":["*/*"],"parameters":[{"name":"id","in":"path","description":"id","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/User"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"put":{"tags":["user-controller"],"summary":"update","operationId":"updateUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"id","in":"path","description":"id","required":true,"type":"integer","format":"int32"},{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"type":"object"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"delete":{"tags":["user-controller"],"summary":"delete","operationId":"deleteUsingDELETE","produces":["*/*"],"parameters":[{"name":"id","in":"path","description":"id","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}},"deprecated":false}}},"definitions":{"User":{"type":"object","properties":{"active":{"type":"integer","format":"int32"},"address":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"id":{"type":"integer","format":"int32"},"lastName":{"type":"string"},"password":{"type":"string"},"phone":{"type":"string"},"photo":{"type":"string"}},"title":"User"}}}




 

Saturday, May 1, 2021

Kotlin - Square Root Naïve Vs Efficient Solution Example

 It was Interview Question for Senior Developer where it was asked like 

Square root

Given an integer, we need to find the floor of its square root.

for example :

I/P = x=4

output = 2

input = x=14

output = 3

input x = 25

output = 5

I will give you 2 solutions for this problem one naive solution and another efficient solution. Let me know if anyone knows another way because to solve this type of problem we need to think of a Binary Search for an efficient solution that reduces the time complexity of the solution.

Naive Solution :

The idea is very simple we check every element one by one if it is square root then we return it.

 eg: lets dry run:

  x=9

 i = 1

 i=2

 i=3

 i=4

 result = (i-1) = 3

 So we come out of the loop when i*i greater than x which means your i-1 is the square root.

 Time Complexity is Theta of square Root of x.

 The solution in Kotlin is very very easy than core-java:

The Efficient Solution can be taken think of  Binar Search 

 So the idea we do x/2 ,x/2... if x/2 more than x then we cut the half.

  lets do the dr run:

 x =10

 low =1

 high=10

 1st Iteration:

  mid =5

 mSq=25

 high =4

  2nd Iteration:

 mid =2

 mSq=4

 low =3 , ans =2

 So Binary Search is a searching algorithm for searching an element in a sorted list or array. Binary Search is efficient than the Linear Search algorithm and performs the search operation in logarithmic time complexity for sorted arrays or lists. Binary Search performs the search operation by repeatedly dividing the search interval in half. The idea is to begin with an interval covering the whole array. If the value of the search key is less than the item in the middle of the interval, narrow the interval to the lower half. Otherwise narrow it to the upper half. Repeatedly check until the value is found or the interval is empty.

Full Code with both Naive vs Efficient Solution:

Runner. kt:

fun main() {
println(sqRootFloor(119))
println(sqareRootFloor(119))
}

/************Naive Solution*****/
fun sqRootFloor(x: Int): Int
{
var i = 1
while (i * i <= x) i++
return i - 1
}

/*************Efficient Solution************/
fun sqareRootFloor(x: Int): Int {
var low = 1
var high = x
var ans = -1
while (low <= high) {
val mid = (low + high) / 2
val mSq = mid * mid
when {
mSq == x -> return mid
mSq > x -> high = mid - 1
else -> {
low = mid + 1
ans = mid
}
}
}
return ans
}

 Input for 4 is 2 you can simply copy-paste and test it out.

Let me know if anyone can come with a more efficient solution for this.