Friday, 26 March 2021

Coding practices to generate report

 Some experience to generate report, for instance, Excel file.

1) simple case: 

    read data from data source (database) using JPA or sql

    process data

    generate report file using data processed

2) case 2

if you need to read relatively large data from DB, and process the data in the code. During the process, you need to access DB multiple times, either read data or write data. If this happens, think about if you can use some sql to do the process in DB side without moving data back and force between DB and application as internet is time consuming.


3) case 3

based on case 2, if the business logic is too complex to process using a few sql statements, think about using temporary table. Populating the temp table from a main table data first, then update columns step by step, based on the needs, you can add some helper columns. If the temp table is correctly populated, you can read data from the table to generate the report file. 

If you need physical data, can consider view as well.

Wednesday, 25 November 2020

Git-two commits both success, but local compile error

 Today got an interesting but kind of weird issue after pull the latest code from git repo.

The auto-build task passed for both two commits but local build fail.

The commit1 has committed Class1, Class2;

The commit2 has committed Class3, but renamed the above classes to class1 and class2 (lower-case). As the two commits at the same time, I suppose the second developer either did not pull the code before push. So his pull reques also got passed. (bad code review here !)


If pull both the commits to local, compile error occurs cause cannot find Class1 and Class2.


Wednesday, 21 October 2020

Customer operation unique id for springfox swagger lib

 when using swagger, each operation can have a id, by default this id is nickname of the annotation, for instance, 

@ApiOperation(value = "create a new user", nickname = "oper_code_create_new_user")

The oper_code_create_new_user will be used as unique id (not sure why the lib use nickname as a unique id but it does). How if you do not add this annotation on the method, for instance, you may have a parent Controller class, and the sub-Controller class just reuse the method without override it. in this case, the unique id will be oper_code_create_new_user_1 by default (as implementated in CachingOperationNameGenerator.java in springfox-spring-web library)

if you want to customize the unique id in the sub-class, for instance, I want to add the sub-class name or the value defined in controller lever tag name which is in @Api annotation, as the suffix,  you can reference below code sneppet.

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class OperationCustomizeUniqueIdReader implements OperationBuilderPlugin {

@Override
public void apply(OperationContext context) {
Optional<ApiOperation> apiOperation = context.findAnnotation(ApiOperation.class);

if (apiOperation.isPresent()) {
Optional<Api> apiOptional = context.findControllerAnnotation(Api.class);
if (apiOptional.isPresent()) {
String tagName = apiOptional.get().tags()[0];
ApiOperation operation = apiOperation.get();
String nickname = operation.nickname();

if (StringUtils.isNotEmpty(nickname) && StringUtils.isNotEmpty(tagName)) {
if (nickname.equalsIgnoreCase("oper_code_create_new_user") ) {
context.operationBuilder().uniqueId(nickname + "_" + tagName);
context.operationBuilder().codegenMethodNameStem(nickname + "_" + tagName);
}
}
}
}
}
@Override
public boolean supports(DocumentationType documentationType) {
return SwaggerPluginSupport.pluginDoesApply(documentationType);
}
}

if you have a sub class with @Api(tags = {"aSubResource"}), then you will get a unique name
for this method "oper_code_create_new_user_aSubResource"

Tuesday, 6 October 2020

Passing parameters in Job and Step level in Spring Batch


1. Job level

Besides passing parameters when building a job, using Job Listener to pass extra job level parameters which can be used through the job running. Specifically, create a class who implements JobExecutionListener, override below method: 

@Override
public void beforeJob(JobExecution jobExecution)

In the method, add more parameters into job context as below:

ExecutionContext jobExecutionContext = jobExecution.getExecutionContext();
jobExecutionContext.put(someKey, someValue);

In Job's steps, using below code to introduce the parameter:

@Value("#{jobExecutionContext[someKey]}")
private SomeCalss someClassInstance; // the value will be someValue

2. Step level

How to pass parameters between Steps within a job?

It is the same as Job level, the Step should implement a Listener StepExecutionListener.

Say if you want to passing a parameter from step1 to step2. Step 1 should implements the above Listener, in afterStep method, add the parameter into execution context:

@Override
public ExitStatus afterStep(StepExecution stepExecution) {
// put a number into job execution context for future use
stepExecution.getJobExecution().getExecutionContext().putLong("aNumber", aNumber);
return null;
}

Step 2 also implements the Listener but in before step method to get the parameter out.

@Override
public void beforeStep(StepExecution stepExecution) {
aNumber = stepExecution.getJobExecution().getExecutionContext().getLong("aNumber");
}

Via this way you can passing parameters from one step to another.








Monday, 25 May 2020

Spring boot Create a Deployable War File

It is easy to follow https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file

But here I want to add one thing is how to access the application context?

One way is you can get the context from below method return:

ConfigurableApplicationContext applicationContext = app.run(args);

Another is if you deploy the .war to external Tomcat server, the applicationContext may be null, in this case, you can get it from onStartup() method

@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
    if (rootAppContext != null) {
        // set context into your own class, say MySpringContext here
        MySpringContext.setContext(rootAppContext);
        servletContext.addListener(new ContextLoaderListener(rootAppContext) {
            @Override            public void contextInitialized(ServletContextEvent event) {
                // no-op because the application context is already initialized            }
        });
    }
    else {
        this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not "                + "return an application context");
    }
}

Saturday, 23 May 2020

Create multiple yml files in Azure pipeline

If you want to have multiple pipeline yml files other than the single default one (with name: azure-pipelines.yml):

Step 1:
Click "pipeline" menu in Azure DevOps, and create a new pipeline yml file. You can rename the yml file with a meanful name, after modify the content, create Save button on the top-right, normally you will choose create a new branch to hold your new created yml file. If this yml is not the default one to be triggered by "- master", you can set
trigger:
none

Step 2:
Run the pipeline to see if it works as your expectation. If it is, you can merge the yml file into "master" branch

Step 3: You can manually run the new created pipeline yml file as per needs.

Normally we only have one yml file with trigger "- master", the other yml files can be triggered manually or per scheduled. As the yml file will be merged into master branch, so it can be ran in master branch, and any branches from master afterwards.


Monday, 13 April 2020

Flowable UI local development

Flowable provides 4 UI applications, take modeler for instance, in order to develop locally:

  1. Create local dir, say my-ui-idm, and copy all contents in flowable-ui-idm into it, as all other UI app needs this idm application, so we have to setup idm first
  2. Create local dir, say my-ui-modeler, and copy all contens in flowable-ui-modeler into it.
  3. Create local dir to hold flowable-spring-boot project.
  4. Modify pom.xml, normally we use a stable version not a SNAPSHOT version, for instance, change all <version> in <parent> to a stable version, for instance, <version>6.5.0</version>, this change will apply to the above three projects
  5. in 1 and 2 projects, you may also need to change <relativePath> value in pom.xml relative to the flowable-spring-boot project
  6. modify flowable-default.properties file if you need, for instance, you want to use a different datasource. This file in both flowable-ui-idm-app/resources and flowable-ui-modeler-app/resources 
  7. run flowable-ui-idm application 
  8. run flowable-ui-modeler application
  9. access url, for instance, http://localhost:8888/flowable-modeler, you may redirect to login page if you first access, and after input user "admin" and password "test" (these all configed in the properties file), you will see modeler process list page.