Posts mit dem Label web development werden angezeigt. Alle Posts anzeigen
Posts mit dem Label web development werden angezeigt. Alle Posts anzeigen

Montag, 13. Juli 2020

My journey with Kotlin JS and React

Honestly I have no idea what the purpose of this post could be, but I am really happy with Kotlin JS and react and I want to write down my thoughts after implementing a real world administration application in my spare time. Maybe some of the sources I link can help somebody, maybe my experience can help someone in a similar position.

This is the app I created:


It uses Kotlin JS, Kotlin react wrapper, react, react-dom, react-router-dom, uuid, styled components, Kotlin coroutines.... the react hooks api, the react context api and Bootstrap 4.

TLDR
: Even though Kotlin JS is still experimental, i created a complete administration application using it and encountered only a few minor bugs. There's so much gain in being able to use gradle tooling and Kotlin as a language for me, that I would happily accept some minor bugs here.

Motivation

I have to admit: I love Kotlin but I was very sceptical about its non-JVM targets, especially after reading a lot about Kotlin Native and its caveats. The thing is: I feel such a strong demand of frontend stuff, that it would be super super handy for a Kotlin team to be able to use the same language - and toolchain - for backend and frontend projects and even share libraries between the two targets. Maintaining builds and code on a high quality level is that much easier this way, polyglot really shows its costs there.

Chicken and egg

I would love to implement such a thing in the team at my company, but this is not an easy task: Kotlin JS is still experimental. The next Kotlin version (1.4) will break binary compatibility for example because the compiler backend is switched completely. Given there is already Typescript, it's very very hard to convince people that another experimental technology might do a good job for the frontend. This results in the chicken-egg-problem: No one tries it, no one knows whether it works out well, no one gains experience, no one helps moving the platform forward, no one makes any progress. Arguing for a new technology reminds me of the time my team switched to Kotlin from Java for the backend. And after we did it, everyone was much happier than before. Getting the time to proof that a techology is worth it and can be used in production is key.

Elephant in the room: JSX

The biggest downside of React with JavaScript is most probably JSX. It reminds me of the wild days when JSP was in. Not only does it require the build system to do very very complex stuff, but also I don't think it's a good idea to extend code with something that makes it code no more, mixing markup languages and programming languages, introducing many strange constructs that are mostly workarounds for naming clashes and identifiers that can't be mapped. Tooling has to be adjusted, knowledge has to be adjusted, code style has to be adjusted... This is a proper comment on that. Do you know what is a nice way to write the UI? kotlinx html. This is basically what everyone would be happy with. I am. There was only one missing piece in the workflow: When you get html based designs of the page you should program, you have to convert html to kotlin dsl. With JSX you can just paste the html into your component and modify it slightly for variable usage. For kotlin dsl, there is this, that lets you do the same, but additionally, you can just start refactor names, extracting methods and so on in the best IDE / one of the best IDEs out there.

Hooks 

The last time I used react was when hooks didn't exist. The usage of setState was so annoying for me, that I just didn't warm up with the framework at all, because state is the single most important aspect of the application code. Hooks are such a nice addition and make functional component usage so pleasant. Take a look at a simple example with kotlin. It's hard not to like that. Now the downside: Hooks have some constraints that are not too intuitive. And now a proper downside: Even though I never used any return statements in kotlin, placed every hook usage at the top of the component, I got the infamous rendered too few hooks error... I wasn't able to figure it out exactly but I suspect it came from nested component usage where I had a fairly complex list based component that nested a lot of stuff. I removed the complex component completely, but if I weren't able to do that because of design requirements, I would have had a hard time with it.

Build

Everyone who knows me knows: Builds are really my métier. I am doing this excessively for many many years with different build systems and I always ensure projects have clean, stable, maintainable builds that enable proper development and testing workflows. Builds are one of those areas where having only one kind of them in the team is very beneficial, as all of the existing tooling can be reused. Being able to use gradle is a big plus for me (note, gradle with kotlin, not gradle with groovy brrrr). Convince yourself of how easy and simple the gradle build of a kotlin js project can be here and here. I can confirm that it works like that for a complete application development cycle. The good thing is, that the whole webpack stuff is hidden from you so that you don't have to bother with the whole mess. However, if necessary, you can configure things. And this is one of the two issues I faced during development: Hot reloading with the webpack development server. I had to apply this workaround as everyone seems to have to. Annoying to find out, not a problem anymore after the small fix.

Final thoughts

What can I say? I am very happy about what can already be done with Kotlin JS. Finally, I can get back to frontend development with pleasure again, keeping all my gradle and kotlin love :)

Donnerstag, 11. Juni 2015

FormGenerator: Automatic html forms from java objects

Purpose

One of Ruby on Rails' features I quite liked but missed in other frameworks, is code generation. The scaffolding produces forms and controllers for a given object automatically and saves the files in appropriate folders. This is mainly convention driven.

With Java, we have a nice type system, so why do I have to write forms over and over again? I used reflection to automatically generate an html form for a given object.

Algorithm

Basically, the FormGenerator gets an arbitrary object attached. On this object, first all fields and inherited fields have to be obtained. Since it would be useless to just process public fields, I had to use a small piece of utility code I found here.



public Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass,
                                     @Nullable Class<?> exclusiveParent) {

  List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
  Class<?> parentClass = startClass.getSuperclass();

  if (parentClass != null &&
     (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
    List<Field> parentClassFields =
                (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
    currentClassFields.addAll(parentClassFields);
  }

  return currentClassFields;
}


After the information about the object is obtained, the form fields are wrapped by a form begin/end pair. The private fields have to be made accassible - note the exception handling.


try {
  field.setAccessible(true);
} catch (SecurityException e) {
  Logger.getGlobal().info(String.format("Field %s can't be accessed, so no input for this field.", field.getName()));
  return result;
}


Depending on the type of the field, inputs should be generated. The types are determined at runtime, so a switch is needed for the value extraction.



if(type.equals(String.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), (String) field.get(object));
} else if(type.equals(Boolean.class)) {
result += generate(formGenerator.getFieldName(field.getName()), (Boolean) field.get(object));
} else if(type.equals(boolean.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), field.getBoolean(object));
} else if(type.equals(Integer.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), (Integer) field.get(object));
} else if(type.equals(int.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), field.getInt(object));
} else if(type.equals(Float.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), (Float) field.get(object));
} else if(type.equals(float.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), field.getFloat(object));
} else if(type.equals(List.class)) {
  result += "<div class=\"well\">" + newLine;
  result += String.format("<div id=\"%s\">%s", formGenerator.getFieldName(field.getName()), newLine);
  result += generate(formGenerator.getFieldName(field.getName()), (List) field.get(object), (ParameterizedType) field.getGenericType());
  result += "</div>" + newLine;
  result += "</div>" + newLine;
}

After all primitive types and lists/collections/iterables or whatever are treated, this method can be called recursively to treat arbitrary classes for fields again. It's probably not the best idea to hardcode css classes into this methods, but for my purposes and right now, bootstrap is the only ui framework I satisfy.

Attention has to be paid for generics. For lists, I implemented a treatment in the following way.



static String generate(String fieldName, List value, ParameterizedType type) {
    StringBuilder builder = new StringBuilder();

    int counter = 0;
    for (Object listItem : value) {
        Class<?> componentClass = (Class<?>) type.getActualTypeArguments()[0];
        String listItemFieldName = fieldName + "_" + counter;
        try {
            Method InputGeneratorMethod = InputGenerator.class.getDeclaredMethod("generate", String.class, componentClass);
            String generatedFormElements = (String) InputGeneratorMethod.invoke(null, listItemFieldName, listItem);
            builder.append(generatedFormElements);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        counter++;
    }

    return builder.toString();
}

The method invocation can be done via reflection again. With the given type, the correct overloaded method is chosen at runtime. However, this could lead to exceptions that one has to handle properly *cough*.

Results

The following class definition is used in my tests.


class MyClass {
    private String testString = "testStringValue";
    private Boolean testBoolean = true;
    private Boolean testBooleanObject = false;
    private int testInt = 12;
    private Integer testInteger = 14;
    private float testFloat = 12.0f;
    private Float testFloatObject = 14.0f;
    private List<String> testStringList = new ArrayList() {{
        add("a0");
        add("a1");
    }};
    private List<Boolean> testBooleanList = new ArrayList() {{
        add(true);
        add(false);
    }};
}

And the generated form looks like this.


It's just an early version yet, there is plenty of stuff left to do. For example the recursive generation for arbirtary objects. Or an injector for style classes. Or field annotations for named fields and non-exported or disabled fields. After this, I'll try to write a reflective argument extractor for ninja, that is capable of parsing request data from generated forms and propagate it back.

Mittwoch, 10. Juni 2015

Ninja framework: Argument extractors

The last post introduced a simple way to automatically extract a collection of objects from a form and inject it into a controller's action. However, when classes get more complex, this option is not the best one because of two reasons: Method signatures get bloated and the collections have to somehow get attached to the corresponding object (injected into the action as well) by hand. If one writes a new action and uses the built in functionality, it's possible that he forgets to update one of the instance's fields....saves...and boom: the object's data is gone.

The functionality can be gathered into an argument extractor. Sadly, the official documentation only shows an example where the session is used to extract a simple session cookie. But what if you have to get complex form data? Ideally, one wants a clean action method signature, where the instance is injected correctly. This can be done with simply with an annotation:


public Result saveTrip(Context context, @argumentextractors.Trip Trip trip) {

It's important to note, that you can't use other built-in extractors (Param, Params) any more, after you parsed the request. Additionally, your own extractor has to be the first extracting paramter in the signature.

The marker interface specifies the extractor class:

@WithArgumentExtractor(TripExtractor.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Trip {
}

The magic then has to be implemented by yourself. Therefore, extend a BodyAsExtractorwhere T is the return type you want to extract. There are three methods to be overriden. I don't have a clue what the third one (getFieldName()) does, but the important one is

public Trip extract(Context context) {}

Your form data has to be gathered now. Took me some time to find out how to do this - actually, you can do


String body = "";
while (context.getReader().ready()) {
  body += context.getReader().readLine();
}

and that's it. I was'n able to use the InputStream the context provides directly. Now the ugly part. the params string of the form http://example.org/path/to/file?a=1&b=2&c=3 should result in a list of a=1, b=2, c=3. Since this is a common task, it's implemented in the apache commons htmlUtils - nice wordplay. I extracted some single methods from their library, because I only use a few ones. Now, you have to apply the parsed values by hand. To mention would be, that this can only work, if the keys you use to extract all the stuff don't change between forms. Otherwise, you would have to implement another extractor.


trip.getStops().clear();
for (int x = 2; x < params.size(); x++) {
  trip.getStops().add(params.get(x).getName());
}
return trip;

The nice thing is now, that everyone who uses this object class, can use the extractor and afterwards just has to save the instance regularly in the controller action:
manager.merge(trip);

I'm curious if this is the intended way to extract stuff from forms. It's a pity that such an important requirement isn't documented better.

Ninja framework: collection extration from forms

Ninja quickly became one of my favorite web frameworks. For REST, MVC, dependency injection, database and other basic stuff, it mostly is very convenient. But what about the more complicated things web development often demands? Because documentation is rather sparse for it, here's how you can use built in functionality to extract a collection of objects from a form.

My example has a simple edit form for a trip model.A trip can have multiple stops, for simplicity represented by a String. With a POST route in the TripsController, Ninja can automatically parse the request, extract your form data and inject the Trip instance into the method call - one has to add a Trip reference the controller's signature and it just works, how great is that:


public Result saveTrip(Context context, Trip trip) {

However, the documentation states, that the extraction only works with primitives and arrays of them. This means no other collections, like Lists, can be extracted automatically. But no one uses plain arrays as fields... So, an easy way to circumvent this limitation, is to add the given items within the collection to the form and provide the same name attribute for all of them:
<#list trip.stops as stop>
  <tr>
    <td><input type="text" class="form-control" id="stops[${stop_index}]" name="stops" value="${stop}" ></td>
  </tr>
</#list>

Then, add the String[] stops parameter to your signature and you're done.


public Result saveTrip(Context context, @Params("stops") String[] stops, Trip trip) {

In my case, I updated all of the trip instance's stops with the stops automatically injected and saved the objet. Can't get any easier, I think.

I'm not yet sure if this would work for more complex (means no-primitive type) objects. For this purpose, argument extractors were introduced. The documentation is again a bit sparse about them - a first try seemed that argument extractors that try to parse the request data for object extraction tend to be a bit hacky. Will be continued.

Donnerstag, 16. April 2015

Simple setup: Spring with Boot, Maven and IntelliJ from scratch

There will never be enough tutorials about how to use Spring with an IDE. Here's another one in case of someone wants to know how to setup a development environment with IntelliJ. Especially the hot reloading features are very important and nobody wants to miss them. Here's how one can do it.


  1. Create a new maven project. Don't use archetypes.
  2. Edit the pom.xml file to use a parent from Spring Boot that does a lot of configuration for you.
       <parent>  
         <groupId>org.springframework.boot</groupId>  
         <artifactId>spring-boot-starter-parent</artifactId>  
         <version>1.1.5.RELEASE</version>  
       </parent>  
    
  3. Also, the spring boot dependency has to be added to the pom.xml.
       <dependencies>  
         <dependency>  
           <groupId>org.springframework.boot</groupId>  
           <artifactId>spring-boot-starter-web</artifactId>  
         </dependency>  
       </dependencies>  
    
  4. The main application class is configured to enable auto configuration. For further information, one should read one of the thousands of Spring tutorials.
     @Configuration  
     @ComponentScan  
     @EnableAutoConfiguration  
     public class Application {  
       public static void main(String[] args) {  
         ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);  
       }  
     }  
    
  5. Now you could add controllers and other stuff. And run the main class from your run configuration. In order to have an executable fat jar, you could use the maven assembly plugin (see one of my other posts). Class reloading with this run configuration should work out of the box.
  6. The convention seems to say one should place resources in src/main/resources/static. Placing an index.html in there will make it available via the applications root path. However, if you use src/main/webapp/ as your folder structure, you fulfill standard java web application convention and make Tomcat automatically recognizing your stuff. You then have to access your static content via /static/index.html or similar, or you can reconfigure your routes (not covered here).
  7. If you work on your static content, you want it to be reloaded automatically. However, this doesn't happen with the configuration so far. That's because of the fact that your static content will be copied into a working directory - changing the root files doesn't change their copies. There may be other ways, I successfully tried to use spring boot maven plugin.
           <plugin>  
             <groupId>org.springframework.boot</groupId>  
             <artifactId>spring-boot-maven-plugin</artifactId>  
             <dependencies>  
               <dependency>  
                 <groupId>org.springframework</groupId>  
                 <artifactId>springloaded</artifactId>  
                 <version>1.2.0.RELEASE</version>  
               </dependency>  
             </dependencies>  
           </plugin>  
    
  8. Executing the goal spring-boot:run in your IDE will now launch the application and automatically reload your content. Debugging with breakpoints and hot reloading your classes  seems to not work with this run configuration any more. But if you want to work on the backend of your application, you could run the main class like before.
If anyone knows a better way to setup a development environment, I would be curious about it, just tell me. Especially it would be nice to have only one run config for reloading static content and classes with breakpoints and stuff alltogether.