Showing posts with label Groovy. Show all posts
Showing posts with label Groovy. Show all posts

Friday, February 8, 2019

GRAILS : Rendering Groovy GSP Code From A String | Grails – Rendering a Template from a String | Render Grails template from external source




package com.pkm.utils

import grails.util.Holders
import groovy.text.Template
import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine
import org.springframework.core.io.ByteArrayResource
import org.springframework.web.context.request.RequestContextHolder

/**
 * Created by pritom on 29/11/2017.
 */
class InvoicePdfUtils extends UtilsBase {
    static def test() {
        GroovyPagesTemplateEngine engine = Holders.applicationContext.getBean(GroovyPagesTemplateEngine)
        StringWriter stringWriter = new StringWriter()

        String content = "CONTENT-\${a}--\${b.none?.none ?: 'None'}-MINE-XXX " +
                "<ui:serverURL/> <g:if test='true'>True</g:if> HERE??? YES ME IS HERE, ".toString()
        Map model = [a: '10', b: [:]]

        String pageName = engine.getCurrentRequestUri(RequestContextHolder.getRequestAttributes().request) // /grails/test.dispatch
        Template template = engine.createTemplate(new ByteArrayResource(content.getBytes("UTF-8"), pageName), pageName, true)
        Writable renderedTemplate = template.make(model)
        renderedTemplate.writeTo(stringWriter)

        println("Request-path=${engine.getCurrentRequestUri(RequestContextHolder.getRequestAttributes().request)}")
        println(stringWriter.toString())
    }
}


This code block will take input from yourself and then parse as a GSP parser and return output as String.

https://little418.com/2009/11/rendering-groovy-gsp-code-from-a-string.html

Tuesday, December 19, 2017

Grails on Groovy: Get Retrieve MySQL Database name from DataSource | Get List of MySQL Tables | Execute MySQL Raw Query




import grails.util.Holders
import org.hibernate.SessionFactory
import org.apache.commons.lang.StringUtils

def dataSource
SessionFactory sessionFactory

String connectionURL = dataSource.targetDataSource.targetDataSource.poolProperties.url
connectionURL = StringUtils.substringAfterLast(connectionURL, '/')
connectionURL = StringUtils.substringBefore(connectionURL, '?')
println(connectionURL)

String databaseName = sessionFactory.currentSession.createSQLQuery("SELECT DATABASE()")
        .setReadOnly(true).setCacheable(false).list().first()
println(databaseName)

String query = "SELECT table_name FROM information_schema.tables WHERE table_schema='$databaseName'".toString()
List list = sessionFactory.currentSession.createSQLQuery(query)
        .setReadOnly(true).setCacheable(false).list()
println(list.size())
println(list)




Sunday, December 3, 2017

GRAILS GROOVY | GString Template Engine | Parse Simple String AS GString Template | String GSP Parser




def gEngine = new groovy.text.GStringTemplateEngine()
def binding = ["a": "a"]
String testVar = gEngine.createTemplate("MY ENGINE STRING AS=\${a}").make(binding).toString()
println(testVar)

Tuesday, November 21, 2017

Grails Groovy Sample DataSource Configuration For Database Connection

You need to edit DataSource.groovy file as below:


dataSource {
    pooled = true
    jmxExport = true
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = "org.hibernate.dialect.MySQL5InnoDBDialect"
    dbCreate = "update"
    username = "root"
    password = ""
    logSql = false
    loggingSql = false
    properties {
        maxActive = 1000
        maxIdle = 100
        minIdle = 50
        initialSize = 1
        minEvictableIdleTimeMillis = 60000
        timeBetweenEvictionRunsMillis = 60000
        numTestsPerEvictionRun = 3
        maxWait = 10000
        testOnBorrow = true
        testWhileIdle = true
        testOnReturn = true
        validationQuery = "SELECT 1"
        minEvictableIdleTimeMillis = 1800000
        timeBetweenEvictionRunsMillis = 1800000
    }
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
    format_sql = false
    use_sql_comments = false
}

// environment specific settings
environments {
    development {
        dataSource {
            url = "jdbc:mysql://localhost/db_name?useUnicode=yes&characterEncoding=UTF-8"
            logSql = true
            loggingSql = true
        }
    }
    test {
        dataSource {
            url = "jdbc:mysql://localhost/db_name?useUnicode=yes&characterEncoding=UTF-8"
            logSql = true
            loggingSql = true
        }
    }
    production {
        dataSource {
            url = "jdbc:mysql://localhost/db_name?useUnicode=yes&characterEncoding=UTF-8"
        }
    }
}
log4j = {
    debug 'org.hibernate.SQL'
    trace 'org.hibernate.type.descriptor.sql.BasicBinder'
}



Grails Groovy Use Groovy Source Class As Singleton Bean | Custom Bean Definition | Transactional Bean



This will act as a transnational bean

First put the below settings to 'Config.groovy'

grails.hibernate.cache.queries = true
grails.databinding.convertEmptyStringsToNull = true

Need to add/modify below contents to 'BuildConfig.groovy' to enable database connection using MYSQL:

grails.project.dependency.resolver = "maven" // or ivy
grails.project.dependency.resolution = {
    // inherit Grails' default dependencies
    inherits("global") {
        // specify dependency exclusions here; for example, uncomment this to disable ehcache:
        // excludes 'ehcache'
    }
    log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
    checksums true // Whether to verify checksums on resolve
    legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility

    repositories {
        inherits true // Whether to inherit repository definitions from plugins

        grailsPlugins()
        grailsHome()
        mavenLocal()
        grailsCentral()
        mavenCentral()
        // uncomment these (or add new ones) to enable remote dependency resolution from public Maven repositories
        //mavenRepo "http://repository.codehaus.org"
        //mavenRepo "http://download.java.net/maven/2/"
        //mavenRepo "http://repository.jboss.com/maven2/"
        mavenRepo "https://oauth.googlecode.com/svn/code/maven"
    }

    dependencies {
        // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g.
        compile "org.springframework:spring-orm:$springVersion"
        runtime 'mysql:mysql-connector-java:5.1.29'
        runtime 'org.springframework:spring-test:4.0.5.RELEASE'
        runtime "commons-httpclient:commons-httpclient:3.1"
        runtime "org.apache.httpcomponents:httpclient:4.3.3"
        runtime 'net.oauth.core:oauth-httpclient4:20090913'
    }

    plugins {
        build ":tomcat:7.0.55"
        runtime ":hibernate4:4.3.6.1"
        compile ":rendering:1.0.0"
        compile ":browser-detection:0.4.3"
        compile ':cache:1.1.8'
        compile ":mail:1.0.5"
        compile ":asset-pipeline:1.9.9"
        compile ':quartz:1.0.2'
    }
}

Below may be you database configuration (DataSource.groovy):

dataSource {
    pooled = true
    jmxExport = true
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = "org.hibernate.dialect.MySQL5InnoDBDialect"
    dbCreate = "update"
    username = "root"
    password = ""
    logSql = false
    loggingSql = false
    properties {
        maxActive = 1000
        maxIdle = 100
        minIdle = 50
        initialSize = 1
        minEvictableIdleTimeMillis = 60000
        timeBetweenEvictionRunsMillis = 60000
        numTestsPerEvictionRun = 3
        maxWait = 10000
        testOnBorrow = true
        testWhileIdle = true
        testOnReturn = true
        validationQuery = "SELECT 1"
        minEvictableIdleTimeMillis = 1800000
        timeBetweenEvictionRunsMillis = 1800000
    }
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
    format_sql = false
    use_sql_comments = false
}

// environment specific settings
environments {
    development {
        dataSource {
            url = "jdbc:mysql://localhost/db_name?useUnicode=yes&characterEncoding=UTF-8"
            logSql = true
            loggingSql = true
        }
    }
    test {
        dataSource {
            url = "jdbc:mysql://localhost/db_name?useUnicode=yes&characterEncoding=UTF-8"
            logSql = true
            loggingSql = true
        }
    }
    production {
        dataSource {
            url = "jdbc:mysql://localhost/db_name?useUnicode=yes&characterEncoding=UTF-8"
        }
    }
}
log4j = {
    debug 'org.hibernate.SQL'
    trace 'org.hibernate.type.descriptor.sql.BasicBinder'
}

Now create a domain class like below:

package com.pritom

class Home {
    Long id
    String name
    String roll

    static constraints = {
        name blank: false
        roll blank: false, unique: true
    }
}

Below is the most important class which will be act as a singleton bean in our system:

package com.pritom

import org.springframework.transaction.annotation.Transactional
/**
 * Created by pritom on 21/11/2017.
 */
@Transactional(rollbackFor = [Exception, NoClassDefFoundError])
class MyBean {
    {
        println("BEAN-INITIALIZATION----------------$from------------------")
    }

    private String from = null

    void setFrom(String x) {
        this.from = x
    }

    void testPrint(String a) {
        println("Bean-class=${this.toString()}===${a}-----------$from")
        Thread.sleep(2000L)
        if (true) new Home(name: "Home-" + Home.count(), roll: String.valueOf(System.currentTimeMillis())).save()
        println("Home-count=${Home.count()}")
        if (true) throw new NoClassDefFoundError("Yeah!!!")
    }
}

We are not completed yet, need to initialize bean from resource.groovy as below:

beans = {
    myBean(MyBean) { bean ->
        bean.scope = 'singleton' // Set bean as singleton
        from = "Resource.groovy" // Set value to a field of class MyBean when initializing
    }
}

And finally test bean from some controller like below:

package com.pritom

class HomeController {
    def myBean

    def index() {
        try {
            myBean.testPrint("1")
            myBean.testPrint("2")
        }
        catch (Throwable ex) {
            println("Error=${ex.getMessage()}")
        }
        render ""
    }
}

And you can download source code from here

Sunday, October 15, 2017

Grails - Register SessionListener as a Spring Bean

Grails - Register SessionListener as a Spring Bean

At first you have to create an listener of session as below:


package com.multidb.listener

import javax.servlet.http.HttpSessionEvent
import javax.servlet.http.HttpSessionListener
/**
 * Created by pritom on 15/10/2017.
 */
class HttpSessionServletListener implements HttpSessionListener {
    @Override
    void sessionCreated(HttpSessionEvent event) {
        println("HttpSessionServletListener:httpSessionCreated:${event.session}")
    }

    @Override
    void sessionDestroyed(HttpSessionEvent event) {
        println("HttpSessionServletListener:sessionDestroyed:${event.session}")
    }
}

Then you need to register in grails-app/conf/spring/resources.groovy as below:


import com.multidb.listener.HttpSessionServletListenererDataSource

beans = {
    httpSessionServletListener(HttpSessionServletListener) {

    }
}

And finally in Bootstrap.groovy


import com.multidb.listener.HttpSessionServletListener
import org.codehaus.groovy.grails.commons.GrailsApplication
import javax.servlet.ServletContext

class BootStrap {
    ServletContext servletContext
    GrailsApplication grailsApplication
    HttpSessionServletListener httpSessionServletListener

    def init = { servletContext ->
        servletContext.addListener(httpSessionServletListener)
    }

    def destroy = {

    }
}

So after an session is created or destroyed your custom listener will be notified.

Tuesday, August 22, 2017

Grails render view from service | Grails Goodness: Render GSP Views And Templates Outside Controllers | Cannot use the session in non-request rendering operations in grails

Grails render view from service | Grails Goodness: Render GSP Views And Templates Outside Controllers | Cannot use the session in non-request rendering operations in grails.


import grails.util.Holders
import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib

class PageRenderedUtils {
    static final String gPath = "org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib"

    static void example() {
        renderTemplate("/user/info", [id: 1L, name: "Pritom Kumar"])
    }

    static String renderTemplate(String template, Map model) {
        return g.render(template: template, model: model)
    }

    static ApplicationTagLib getG() {
        return (ApplicationTagLib) Holders.applicationContext.getBean(gPath)
    }
}

Saturday, August 5, 2017

Grails Groovy: Execute Code transparent before and after any method is invoked | Override Method Implementation | Override Method Body

Grails Groovy: Execute Code transparent before and after any method is invoked | Override Method Implementation | Override Method Body.
Note:-
  • Make sure the returned value from the method call is returned from the closure as well
  • Might be a expensive of a class has number of methods with heavy implementation
  • If selective execution is required, then check for the method name and then intercept the call


class Dummy {
    def method1() { 
        System.out.println "In method 1"
    }

    def method2(String str) { 
        System.out.println "In method 2"
    }
    
    static def method3(int a, int b) {
        System.out.println "In static method 3" 
    }    
}

Dummy.metaClass.invokeMethod = {String name, List args ->
   def result = null

   System.out.println("Do something before $name is called with args $args")

   try {
       result = delegate.metaClass.getMetaMethod(name, args).invoke(delegate, args)
   }
   catch(Exception e) {
        System.out.println "Handling exception for method $name"
   }

   System.out.println("Do something after $name was called with args $args")

   return result
}

Dummy.metaClass.'static'.invokeMethod = {String name, List args ->
   def result = null

   System.out.println("Do something before static method $name is called with args $args")

   try {
       result = delegate.metaClass.getMetaMethod(name, args).invoke(delegate, args)
   }
   catch(Exception e) {
        System.out.println "Handling exception for method $name"
   }

   System.out.println("Do something after static method $name was called with args $args")

   return result
}

def dummy = new Dummy()
dummy.method1()
dummy.method2('Test')
Dummy.method3(1, 2)

Thursday, July 27, 2017

Grails Groovy SessionFactory EnityKey SessionStatistics | Grails Get SQL Table Name From Domain Class | Grails Get SQL Table Field Name From Domain Class

Grails Groovy SessionFactory EnityKey SessionStatistics | Grails Get SQL Table Name From Domain Class | Grails Get SQL Table Field Name From Domain Class.


import org.hibernate.SessionFactory
import grails.util.Holders
import org.hibernate.engine.spi.EntityKey
import org.hibernate.stat.SessionStatistics
/**
 * Created by pritom on 27/07/2017.
 */
class HibernateSessionUtil {
    private static SessionFactory _sessionFactory

    public static void main(String[] args) {
        def domainInstance = "DomainClass".proxy(100L)
        checkIfObjectExistsInSession(domainInstance)
    }

    static void evictAllEntities() {
        SessionStatistics sessionStatistics = sessionFactory.currentSession.getStatistics()
        sessionStatistics.getEntityKeys().asList().each { EntityKey entityKey ->
            evict(entityKey.persisterClass.proxy(entityKey.identifier.toString().toLong()))
        }
    }

    static void evict(def instance) {
        sessionFactory.currentSession.evict(instance)
    }

    static Boolean checkIfObjectExistsInSession(def domainInstance) {
        SessionStatistics sessionStatistics = sessionFactory.currentSession.getStatistics()
        println("Total ${sessionStatistics.getEntityKeys().asList().size()} Object Exists in Session")
        Boolean exists = false
        sessionStatistics.getEntityKeys().asList().find { EntityKey entityKey ->
            println("EntityName=${entityKey.entityName},EntityId=${entityKey.identifier.toString()}")
            if (domainInstance.class.canonicalName.equals(entityKey.entityName) && domainInstance.id.toString().equals(entityKey.identifier.toString())) {
                exists = true
            }
        }
        return exists
    }

    static String getTableFieldName(Class clazz, String fieldName) {
        return sessionFactory.getClassMetadata(clazz).propertyMapping.getColumnNames(fieldName)[0]
    }

    static String getTableName(Class clazz) {
        return sessionFactory.getClassMetadata(clazz).getTableName()
    }

    static boolean flushAndClearCache() {
        try {
            sessionFactory.currentSession.flush()
            sessionFactory.currentSession.clear()
            sessionFactory.getCache().evictEntityRegions()
            sessionFactory.getCache().evictCollectionRegions()
            sessionFactory.getCache().evictDefaultQueryRegion()
            sessionFactory.getCache().evictQueryRegions()
            return true
        }
        catch (Exception ex) {
            ex.printStackTrace()
            return false
        }
    }

    static SessionFactory getSessionFactory() {
        _sessionFactory = _sessionFactory ?: (_sessionFactory = Holders.applicationContext.getBean(SessionFactory))
    }

    static {
        EntityKey.metaClass.getPersisterClass = {
            return persister.entityTuplizer.mappedClass
        }
    }
}

c

Grails Groovy Hibernate | Hibernate Criteria Builder | Projection | Custom Projection | Group By Projection | PropertyProjection

Grails Groovy Hibernate | Hibernate Criteria Builder | Projection | Custom Projection | Group By Projection | PropertyProjection.

It's easy to add projection custom. We can add custom projection and custom group by property.


import org.hibernate.criterion.Projections
import org.hibernate.criterion.Projection
import org.hibernate.criterion.PropertyProjection
import org.hibernate.Criteria
import org.hibernate.criterion.CriteriaQuery
import org.hibernate.HibernateException
org.hibernate.criterion.ProjectionList projectionList = []

ProjectionList projectionList = []
projectionList.add(new PropertyProjection("id") {
    @Override
    public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException {
        return "this_.id as y0_"
    }
})
projectionList.add(new PropertyProjection("created") {
    @Override
    public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException {
        return "MAX(this_.created) as y1_"
    }
})
projectionList.add(Projections.groupProperty("belongsTo.id"))
PropertyProjection groupBy = new PropertyProjection("id", true) {
    @Override
    public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException {
        return "belongsto1_.id as y1_"
    }

    @Override
    public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        return "belongsto1_.id"
    }
}
projectionList.add(groupBy)
Closure closure = {
    setProjection(projectionList)
}
List list =  Domain.createCriteria().list {
    and closure
}



But you want to make it simpler then you can use below function easily:


import org.hibernate.criterion.CriteriaSpecification
import org.hibernate.criterion.Projections
import org.hibernate.type.DoubleType
import org.hibernate.type.Type

resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
projections {
    groupProperty("id", "id")
    groupProperty("name", "name")
    addProjectionToList(Projections.sqlProjection(
            "sum(id * 0.2) as totalPrice",
            ["totalPrice"] as String[],
            [DoubleType.INSTANCE] as Type[],
    ), "complexSqlCalculation")
}

Grails | Groovy | Create Criteria | Hibernate Criteria Builder | Custom Criteria Order | Custom Sort By | Custom Order Criteria

Grails | Groovy | Create Criteria | Hibernate Criteria Builder | Custom Criteria Order | Custom Sort By | Custom Order Criteria. 

In Grails we may need sometime to add sort / order by with some aggregate function as sum of two fields. Suppose we have a Grails / Groovy domain which has two field named "amount" and "tax", now we want to sort by sum of these two fields. So we can do that using below sample code: 


import groovy.lang.Closure
import org.hibernate.Criteria
import org.hibernate.HibernateException
import org.hibernate.criterion.CriteriaQuery
import org.hibernate.criterion.Order as CriterionOrder

Closure closure = {
    addOrder(new CriterionOrder("amount", params.dir) {
        @Override
        String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        return "(this_.amount + this_.tax) asc"
        }
    })
    projections {
        property("id")
        property("amount")
    }
}
List list = Domain.createCriteria().list {
    and closure
}

Monday, November 7, 2016

Grails send email using grails.plugin.mail.MailService and track unique message ID

At first have to extend "grails.plugin.mail.MailService" as follows:

class MyMailService extends MailService

MyMailService would be like:

package com.pkm.services.mail

import grails.plugin.mail.MailMessageBuilder
import grails.plugin.mail.MailService
import org.springframework.mail.MailMessage
import org.springframework.mail.javamail.MimeMessageHelper

class MyMailService extends MailService {
    MailMessage sendMail(Closure callable) {
        if (isDisabled()) {
            throw new Exception("Sending emails disabled by configuration option")
            return null
        }

        MailMessageBuilder messageBuilder = mailMessageBuilderFactory.createBuilder(mailConfig)
        messageBuilder.multipart(true)

        /* Sending some custom headers with this email */
        Map<String, String> v = new HashMap<String,String>()
        v.put("header_1", "Header_1")
        v.put("header_2", "Header_2")
        messageBuilder.headers(v)

        callable.delegate = messageBuilder
        callable.resolveStrategy = Closure.DELEGATE_FIRST
        callable.call(messageBuilder)

        MailMessage mailMessage = messageBuilder.sendMessage(mailExecutorService)

        /* Reading unique message ID */
        MimeMessageHelper mimeMessageHelper = messageBuilder.getProperty("helper")
        String messageID = mimeMessageHelper.mimeMessage.getHeader("Message-ID").toString().trim()
        /* Now you can do your stuff with messageID */

        return mailMessage
    }
}
Use below code snippet to send mail
myMailService.sendMail() {
    mailSender.host = "host.mail.com"
    mailSender.javaMailProperties.put("mail.smtp.auth", "true")
    mailSender.username = "pritomkucse@gmail.com"
    mailSender.password = "xxxxxxxxx"
    mailSender.port = 569

    multipart true

    from "Pritom<pritomkucse@gmail.com>"
    to
    replyTo
    cc
    bcc
    subject
    text
    html
    attachBytes "some.name" "file_type" fileBytes
}
You need to add following dependency to BuildConfig.groovy as follows:
grails.project.dependency.resolution = {
    // inherit Grails' default dependencies
    inherits("global") {
        // specify dependency exclusions here; for example, uncomment this to disable ehcache:
        // excludes 'ehcache'
    }
    log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
    checksums true // Whether to verify checksums on resolve
    legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility

    repositories {
        inherits true // Whether to inherit repository definitions from plugins

        grailsPlugins()
        grailsHome()
        mavenLocal()
        grailsCentral()
        mavenCentral()
        // uncomment these (or add new ones) to enable remote dependency resolution from public Maven repositories
        //mavenRepo "http://repository.codehaus.org"
        //mavenRepo "http://download.java.net/maven/2/"
        //mavenRepo "http://repository.jboss.com/maven2/"
    }

    dependencies {
        // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g.
        // runtime 'mysql:mysql-connector-java:5.1.29'
        // runtime 'org.postgresql:postgresql:9.3-1101-jdbc41'
        test "org.grails:grails-datastore-test-support:1.0.2-grails-2.4"
        compile "org.springframework:spring-orm:$springVersion"
        runtime 'mysql:mysql-connector-java:5.1.29'
    }

    plugins {
        // plugins for the build system only
        build ":tomcat:7.0.55"

        // plugins for the compile step
        compile ":scaffolding:2.1.2"
        compile ':cache:1.1.8'
        compile ":asset-pipeline:1.9.9"
        compile ":mail:1.0.5"

        // plugins needed at runtime but not for compilation
        runtime ":hibernate4:4.3.6.1" // or ":hibernate:3.6.10.18"
        runtime ":database-migration:1.4.0"
        runtime ":jquery:1.11.1"

        // Uncomment these to enable additional asset-pipeline capabilities
        //compile ":sass-asset-pipeline:1.9.0"
        //compile ":less-asset-pipeline:1.10.0"
        //compile ":coffee-asset-pipeline:1.8.0"
        //compile ":handlebars-asset-pipeline:1.3.0.3"
    }
}