Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

2023-02-21

Spring Boot: Application Event Lifecycle

 With @EventListener, we can quickly turn a component's method into an event listener. Spring Boot has some predefined events we can tap into; they mark milestones in the application lifecycle.

  1. ApplicationStartingEvent
    Only the bootstrap context has been created, and "java.awt.headless" is turned off unless explicitly enabled externally.
  2. ApplicationEnvironmentPreparedEvent
    The environment sources are parsed and configured.
    Now you still have the opportunity to alter the environment, as the binding is a later step.
  3. ApplicationContextInitializedEvent
    Context is prepared, the environment is applied, and initializers have been executed. After this event, the bootstrap context is discarded.
  4. ApplicationPreparedEvent
    Beans were loaded, the magic happened.
  5. ApplicationStartedEvent
    The application context is refreshed, and we're alive.
    After this event is published, the liveness state is set to "correct" since there were no failures during initialization.
  6. On error between steps 2-5, an ApplicationFailedEvent is published before the shutdown hook is executed.
  7. ApplicationReadyEvent
    The startup is done, measurements are taken, startup info is logged, and runners are executed (command line or job). The readiness state is set to "accepting traffic" to let external actors know we're online.
After the started event, not much happens that affect us if we are making a standard application, so if we want to load some data that we couldn't or wouldn't initialize lazily on its own service, this is the spot.

2022-05-04

Java Memory Usage Optimization

So there is this not really well-known but existing memory usage optimization that changes how Glibc allocated thread-specific memory.

There is this guy who wrote the best roundup I found on the net so far: Major Bug in glibc is Killing Applications With a Memory Limit. I strongly suggest reading it.

For now, let me just quote the important part:

Long story short, this is due to a bug in malloc(). Well, it’s not a bug it’s a feature.

malloc() preallocates large chunks of memory, per thread. This is meant as a performance optimization, to reduce memory contention in highly threaded applications.

In 32 bits runtime, it can preallocate up to 2 * 64 MB * cores.

In 64 bits runtime, it can preallocate up to 8 * 64 MB * cores.

So the math is like: _NPROCESSORS_ONLN * $MALLOC_ARENA_MAX * Arena Size

Bonus content: As getconf _NPROCESSORS_ONLN returns the same as nproc output (well, almost, because nproc returns sysconf(_SC_NPROCESSORS_CONF)), if you are using a container engine like Kubernetes, this equation will use the node's core count, not the CPU shares allowed by cgroups to the pod.

Where do those numbers come from? check here: https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html

Arena Size is usually 64MB. Why is this a problem?

The first malloc in each thread triggers a 128MB mmap which typically is the initialization of thread-local storage.

-- https://bugs.openjdk.java.net/browse/JDK-8193521

For every thread created, a new arena is allocated. But even if you don't make any threads, the preallocation happens using the equation above. Huge memory waste.

If creating more arenas is denied, the thread instead writes to "main" arena or the native program heap, which is unbounded.

(Main arena can grow via brk()/sbrk())

So the most useful solution is to set the environment variable MALLOC_ARENA_MAX to a small value, like 4.

2020-04-20

Spring: Implicit CLOB to String

So you have a stored procedure, which has an OUT parameter, with the type of CLOB.
You want to get the contents. It seems easy, right?

Well, you just need to actually ask Hibernate to turn it into a String for you in one easy step, using org.hibernate.type.MaterializedClobType:

PROCEDURE that_one_procedure(
    P_ARG1        IN  VARCHAR2,
    P_ARG2        IN  VARCHAR2,
    P_ARG3        IN  VARCHAR2,
    X_SOME_OUTPUT OUT NOCOPY CLOB
);

// Excerpt of an Entity class...
import org.hibernate.type.MaterializedClobType;

@NamedStoredProcedureQuery(name = "querySomeProcedure", procedureName = "MY_PACKAGE.THAT_ONE_PROCEDURE",
 parameters = {
   @StoredProcedureParameter(name = "P_ARG1", 
     type = String.class),
   @StoredProcedureParameter(name = "P_ARG2", 
     type = String.class),
   @StoredProcedureParameter(name = "P_ARG3", 
     type = String.class),
   @StoredProcedureParameter(name = "X_SOME_OUTPUT", 
     mode = ParameterMode.OUT, 
     type = MaterializedClobType.class) /* <-- !!! */

Now my Repository can look like this:
// Excerpt of a Repository...
@Procedure(name = "querySomeProcedure", outputParameterName = "X_SOME_OUTPUT")
 String querySomeProcedure(@Param("P_ARG1") String someArg,
   @Param("P_ARG2") String thatAnotherArg,
   @Param("P_ARG3") String alsoAnArg);

See, all the magic is done by Hibernate.

2019-01-10

JDeveloper: JboConfigUtil Exception

So you were tinkering with JDeveloper (any version), and while trying to use an online database for your new entity / whatever, you instead got this:

SEVERE: null at oracle.jbo.dt.objects.config.JboConfigUtil.getConfigurationNameList(JboConfigUtil.java:235)
java.lang.NullPointerException
 at oracle.jbo.dt.objects.config.JboConfigUtil.getConfigurationNameList(JboConfigUtil.java:235)
 at oracle.jbo.dt.objects.config.JboConfigUtil.getConfigurationNameList(JboConfigUtil.java:225)
 at oracle.jbo.dt.jdevx.ui.pkg.XPKConnectPanel.updateConfigurations(XPKConnectPanel.java:703)
 at oracle.jbo.dt.jdevx.ui.pkg.XPKConnectPanel.onFinish(XPKConnectPanel.java:684)
 at oracle.jbo.dt.ui.main.dlg.DtuWizardPanelDialog.okAction(DtuWizardPanelDialog.java:362)
 at oracle.jbo.dt.ui.main.dlg.DtjDialog.dismissDialog(DtjDialog.java:221)
 (...)

You've already created the data source, set it in Project Properties [on the model project] / ADF Business Components, reported the feedback, saw it will be fixed in JDeveloper 13... but until then:

Go to the .jpx file of your model project (the file with the two cogwheels icon), General / Connection, select the connection.

2018-04-19

Java 8: JDBC ResultSet to Stream

Streams API is a gift.
But JDBC is still the old-school one.
Let's wrap it!

package org.example.jdbc.stream;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public final class StreamHelper {
    public static class Record {
        private final Map<String, Object> fields = new HashMap<>(16);
        private final long count;

        private Record(final ResultSet resultSet) throws SQLException {
            final ResultSetMetaData metaData = resultSet.getMetaData();
            count = metaData.getColumnCount();
            for (int i = 1; i <= count; ++i) {
                fields.put(metaData.getColumnName(i), resultSet.getObject(i));
            }
        }

        /**
         * Is there a column named like this?
         *
         * @param columnName is the column name in the query.
         * @return True if found.
         */
        public boolean contains(final String columnName) {
            return fields.containsKey(columnName);
        }

        /**
         * Number of columns.
         *
         * @return Numer of columns.
         */
        public long count() {
            return count;
        }

        /**
         * Get value casted to the requested type.
         * <p>
         * No type checking happens inside. It is your job to know the datatype in the database.
         * <p>
         * Example:<br>
         * {@code record.get("COLUMN1", Long.class); // returns a Long}
         *
         * @param columnName is the column name in the query.
         * @param type is Java type of the column.
         * @return The value casted to the Java type.
         */
        public <T> T get(final String columnName, final Class<T> type) {
            return type.cast(getObject(columnName));
        }

        /**
         * Get columns in the record.
         *
         * @return Collection of the column names.
         */
        public Set<String> getColumns() {
            return Collections.unmodifiableSet(fields.keySet());
        }

        /**
         * Get value as an object.
         *
         * @param columnName is the column name in the query.
         * @return The value.
         */
        public Object getObject(final String columnName) {
            return fields.get(columnName);
        }

        /**
         * Get value as string.
         *
         * @param columnName is the column name in the query.
         * @return Value as string.
         */
        public String getString(final String columnName) {
            return Objects.toString(fields.get(columnName));
        }

        /**
         * Is the given cell null?
         *
         * @param columnName is the column name in the query.
         * @return True if null.
         */
        public boolean isNull(final String columnName) {
            return getObject(columnName) == null;
        }

        @Override
        public String toString() {
            return fields.entrySet().stream().map(e -> e.getKey() + ": " + e.getValue())
                    .collect(Collectors.joining(", "));
        }
    }

    /**
     * Wrap a ResultSet in a Stream.
     * <p>
     * The wrapper consumes the result set. The caller must close the result set after the stream
     * processing was finished.
     *
     * @param resultSet is the open result set to streamline.
     * @return A stream of rows.
     */
    public static Stream<Record> asStream(final ResultSet resultSet) {
        // "est = Long.MAX_VALUE if infinite, unknown, or too expensive to compute."
        return StreamSupport.stream(new Spliterators.AbstractSpliterator<Record>(Long.MAX_VALUE,
                Spliterator.NONNULL | Spliterator.IMMUTABLE) {
            @Override
            public boolean tryAdvance(final Consumer<? super Record> action) {
                try {
                    if (!resultSet.next()) {
                        return false;
                    }
                } catch (@SuppressWarnings("unused") final SQLException e) {
                    return false;
                }
                try {
                    action.accept(new Record(resultSet));
                } catch (@SuppressWarnings("unused") final SQLException e) {
                    return false;
                }
                return true;
            }
        }, true).parallel();
    }

    private StreamHelper() { /* Hidden. */ }
}
Usage example:
import org.example.jdbc.stream.StreamHelper;
import org.example.jdbc.stream.StreamHelper.Record;
// ...
    @GET
    @Path("test")
    public Response test() {
        try (Connection connection = Database.connect();
                Statement statement = connection.createStatement();
                ResultSet resultSet = statement.executeQuery("SELECT * FROM MY_TABLE")) {
            return Response.ok(StreamHelper.asStream(resultSet).map(Record::toString)
                    .collect(Collectors.joining(", "))).build();
        } catch (final SQLException | NamingException e) {
            return Response.serverError().entity(e.toString()).build();
        }
    }

2018-04-05

Java EE7 ScheduledManagedExecutorService



 CustomWorkManager
 
  MINTHREAD
  10
 
 
  MAXTHREAD
  150
 
 
  MAXCAPACITY
  10000
 



 FooSMES
 CustomWorkManager
 10



 concurrent/FooSMES
 FooSMES



 concurrent/FooSMES
 javax.enterprise.concurrent.ManagedScheduledExecutorService

ScheduledExecutorService mySMES = InitialContext.doLookup("java:comp/env/concurrent/FooSMES");

ScheduledFuture<?> future = mySMES.scheduleAtFixedRate(SomeClass::someMethod, 0, 5, TimeUnit.MINUTES);
// Starts immediately (delay 0 minutes), run every 5 minutes.

// How to stop:
if (!future.isCancelled()) {
 future.cancel(true);
}
// true == If the Runnable is running at the moment, the Thread will likely receive an interrupt.
// Inside SomeClass's someMethod method.

// How to handle thread interruption:
// 1) Periodically check Thread.interrupted(). If it's true, please quickly clean up, and then return.
// 2) catch InterruptedException -- this is not available at all places.

// Example:
while(!Thread.interrupted()) {
 // Do repetitive tasks that took very long if you consider all the iterations combined.
}
// We get here when interrupted. Clean up everything and leave.

2017-12-28

Java EE7 ManagedExecutorService

So you have a WebLogic and want to try out the fancy "new" feature of having managed threads.

Have the ExecutorService configured first in weblogic.xml.
<!-- weblogic.xml -->
<managed-executor-service>
 <name>CustomMES</name>
 <max-concurrent-long-running-requests>10</max-concurrent-long-running-requests>
</managed-executor-service>
<resource-env-description>
 <resource-env-ref-name>concurrent/CustomMES</resource-env-ref-name>
 <resource-link>CustomMES</resource-link>
</resource-env-description>

Then name it in web.xml.
<!-- web.xml -->
<resource-env-ref>
 <resource-env-ref-name>concurrent/CustomMES</resource-env-ref-name>
 <resource-env-ref-type>javax.enterprise.concurrent.ManagedExecutorService</resource-env-ref-type>
</resource-env-ref>

Now, you can ask for it.
public static ExecutorService getExecutorService() {
 try {
  // Get our own, defined in weblogic.xml
  return InitialContext.doLookup("java:comp/env/concurrent/CustomMES");
 } catch (final NamingException e) {}
 try {
  // Fallback level 1: Get the Java EE 7 default MES
  return InitialContext.doLookup("java:comp/DefaultManagedExecutorService");
 } catch (final NamingException e) {}
 // Fallback level 2: Return the default pool. 
 // This always exists. In fact, every Java8 threaded feature uses this.
 return ForkJoinPool.commonPool();
}
In the latter case, you could use the @Resource annotation with the mappedBy attribute, if you have properly configured beans.

The ExecutorService is already an Executor, so you can pass it to eg. a CompletableFuture:
CompletableFuture<void> future = CompletableFuture
            .supplyAsync(Frobolator::aSupplier, getExecutorService())
            .thenAccept(result -> bar(result));

// Later, wait for the Future, if necessary.
future.join();

But properly usingCompletableFutures are another topic...

Implementation of the ManagedExecutorService in WebLogic is here: $ORACLE_HOME\wlserver\modules\com.oracle.weblogic.concurrent.jar

2017-11-13

JDeveloper: bindingContext.findDataControl deprecated

You get a warning, that says:
Warning: findDataControl(java.lang.String) in oracle.adf.model.BindingContext has been deprecated.
So you need another, up-to-date way to get your data control.
Try this:

private static ApplicationModuleImpl getAppModuleImpl(final String dataControlName) {
    final FacesContext fc = FacesContext.getCurrentInstance();
    final ELContext elContext = fc.getELContext();
    final ExpressionFactory elFactory = fc.getApplication().getExpressionFactory();
    final ValueExpression valueExp = elFactory.createValueExpression(elContext, "#{data." + dataControlName + ".dataProvider}", ApplicationModuleImpl.class);
    return (ApplicationModuleImpl) valueExp.getValue(elContext);
}

Usually, the data control name is like {Application Module name} + "DataControl".
Hard to find? Look into DataBindings.cpx and look for BC4JDataControl elements id attribute.

What's that data literal there? That's oracle.adf.model.BindingContext.CONTEXT_ID hardcoded.

2017-11-10

Java: Jersey-server Async

Have you heard about Jersey's @ManagedAsync annotation?

What does it give? Well, it automagically runs the resource method in a separate thread, hosted by Jersey's own ExecutorService.
This way you don't have to do a boilerplate executor pattern for your every resource method.

When is this useful: If you expect blocking IO during handling the request, this way you can easily offload the execution into a different thread that can block as much as it wants.

For beautiful examples, see this blog post.

2017-10-25

JDeveloper: Build Does Not Find File That Dont Even Exist


Your build fails, as JDev cannot copy a file that does not even exist? The JDeveloper's IDE Performance Cache is playing you.

Simple solution:

  1. Exit JDeveloper
  2. Delete the .data folder inside your app folder.
  3. Profit!
If it's not there, see the source for detailed info.

Source (Thanks, Andrejus!)

2017-08-30

Weblogic: JAR entry META-INF/adfm.xml not found in _wl_cls_gen.jar

You've just deployed your favorite EAR in a WebLogic successfully, but when you open it, an unfriendly HTTP 500 greets you. This usually happens in development mode.
Error 500--Internal Server Error

oracle.jbo.JboException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-29000. Error message parameters are {0=java.io.FileNotFoundException, 1=JAR entry META-INF/adfm.xml not found in C:\Users\yolo\AppData\Roaming\JDeveloper\system12.2.1.2.42.170105.1224\DefaultDomain\servers\DefaultServer\tmp\_WL_user\OalCdqWorkbench\dfoxis\war\WEB-INF\lib\_wl_cls_gen.jar}
 at oracle.jbo.uicli.mom.CpxUtils.getCpxListFromMetadata(CpxUtils.java:512)
 at oracle.jbo.uicli.mom.JUMetaObjectManager.loadCpx(JUMetaObjectManager.java:915)
 at oracle.adf.model.BindingContext.initialize(BindingContext.java:469)
 at oracle.adf.model.BindingRequestHandler.beginRequest(BindingRequestHandler.java:270)
 at oracle.adf.model.servlet.ADFBindingFilter.doFilter(ADFBindingFilter.java:196)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.adfinternal.view.faces.webapp.rich.RegistrationFilter.doFilter(RegistrationFilter.java:105)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:529)
 at oracle.adfinternal.view.faces.activedata.AdsFilter.doFilter(AdsFilter.java:60)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:529)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:354)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:232)
 at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.adf.library.webapp.LibraryFilter.doFilter(LibraryFilter.java:169)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:141)
 at java.security.AccessController.doPrivileged(Native Method)
 at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:315)
 at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:650)
 at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:124)
 at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:232)
 at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:94)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:248)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:32)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3683)
 at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3649)
 at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:326)
 at weblogic.security.service.SecurityManager.runAsForUserCode(SecurityManager.java:197)
 at weblogic.servlet.provider.WlsSecurityProvider.runAsForUserCode(WlsSecurityProvider.java:203)
 at weblogic.servlet.provider.WlsSubjectHandle.run(WlsSubjectHandle.java:71)
 at weblogic.servlet.internal.WebAppServletContext.doSecuredExecute(WebAppServletContext.java:2433)
 at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2281)
 at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2259)
 at weblogic.servlet.internal.ServletRequestImpl.runInternal(ServletRequestImpl.java:1691)
 at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1651)
 at weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor.run(ContainerSupportProviderImpl.java:270)
 at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:348)
 at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:333)
 at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:54)
 at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
 at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:640)
 at weblogic.work.ExecuteThread.execute(ExecuteThread.java:406)
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:346)
Caused by: java.io.FileNotFoundException: JAR entry META-INF/adfm.xml not found in C:\Users\yolo\AppData\Roaming\JDeveloper\system12.2.1.2.42.170105.1224\DefaultDomain\servers\DefaultServer\tmp\_WL_user\OalCdqWorkbench\dfoxis\war\WEB-INF\lib\_wl_cls_gen.jar
 at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:142)
 at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:150)
 at java.net.URL.openStream(URL.java:1045)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.openStream(MetadataRegistryImpl.java:650)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.getDomDocument(MetadataRegistryImpl.java:665)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.getRegistryPaths(MetadataRegistryImpl.java:247)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.visitRegistryPaths(MetadataRegistryImpl.java:160)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.visitRegistryPaths(MetadataRegistryImpl.java:116)
 at oracle.jbo.uicli.mom.CpxUtils.getCpxListFromMetadata(CpxUtils.java:491)
 ... 46 more

What to do: Restart WebLogic.

The issue is handled here: https://support.oracle.com/knowledge/Middleware/2244836_1.html
This is a caching issue, so to permanently fix it, upgrade your JDeveloper.

2017-08-23

Java: CORS Filter for Jersey

So you want to add CORS support for your Jersey server.

Add this filter:

package org.example.rest.service.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import org.glassfish.jersey.server.ExtendedUriInfo;

@Provider
@Priority(Priorities.HEADER_DECORATOR)
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
 private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
 private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
 private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
 private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
 private static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
 private static final String AUTHORIZATION = "authorization";
 private static final String ORIGIN = "Origin";

 private static String extractAllowedMethods(final ExtendedUriInfo extendedUriInfo) {
  final Optional<Class<?>> optional = extendedUriInfo.getMatchedRuntimeResources().stream()
    .flatMap(r -> r.getResources().stream()).flatMap(r -> r.getHandlerClasses().stream())
    .filter(r -> r.getPackage().getName().startsWith("org.example.rest.service")).findFirst();

  if (optional.isPresent()) {
   return Arrays.stream(optional.get().getDeclaredMethods())//
     .flatMap(m -> Arrays.stream(m.getAnnotations()))//
     .map(a -> a.annotationType().getAnnotation(javax.ws.rs.HttpMethod.class))//
     .filter(Objects::nonNull)//
     .map(HttpMethod::value)//
     .distinct()//
     .collect(Collectors.joining(", "));
  }
  // Returning OPTIONS is a bit shady, as ACAM is about *real*, actual methods only.
  return "OPTIONS";
 }

 @Context
 private HttpServletRequest request;

 @Context
 private ExtendedUriInfo extendedUriInfo;

 @Override
 public void filter(final ContainerRequestContext requestContext) throws IOException {
  final String origin = requestContext.getHeaderString(ORIGIN);
  if (origin != null && "OPTIONS".equals(requestContext.getMethod())) {
   request.setAttribute(this.getClass().getName(), true);
   requestContext.abortWith(Response.ok("CORS OK, carry on.", MediaType.TEXT_PLAIN_TYPE).build());
  }
 }

 /**
  * @see https://www.w3.org/TR/cors/
  * @see https://jmchung.github.io/blog/2013/08/11/cross-domain-on-jersey-restful-web-services/
  * @see https://solutionsisee.wordpress.com/2016/06/30/adding-cors-support-in-jersey-server/
  */
 @Override
 public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
   throws IOException {

  final MultivaluedMap<String, Object> responseHeaders = responseContext.getHeaders();
  final String origin = requestContext.getHeaderString(ORIGIN);

  if (origin != null) {
   // The presence of the Origin header marks a CORS request.
   responseHeaders.add(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
   responseHeaders.add(ACCESS_CONTROL_ALLOW_METHODS, extractAllowedMethods(extendedUriInfo));
   responseHeaders.add(ACCESS_CONTROL_ALLOW_HEADERS, AUTHORIZATION + ", X-Requested-With, Content-Type");
   if (requestContext.getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS) != null) {
    responseHeaders.add(ACCESS_CONTROL_ALLOW_CREDENTIALS, requestContext
      .getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS).toLowerCase().contains(AUTHORIZATION));
   }
  }

  if (request.getAttribute(this.getClass().getName()) != null) {
   // We are in a CORS Preflight answer, fast tracked. The entity (== response body) is not
   // relevant.
  }
 }
}

It is almost implementation agnostic, except for the ExtendedUriInfo.

2017-07-05

Maven: Using Oracle Archetypes with Maven in Eclipse


  1. Go here: https://community.oracle.com/docs/DOC-894569
  2. Download this file: https://maven.oracle.com/archetype-catalog.xml
    1. It will ask for your OTN login, of course.
    2. If you don't download it, you will not be able to use it.
  3. During archetype selection, click Configure... next to the Catalog drop-down.
  4. Add Local Catalog...
    1. Adding a remote catalog is not working because of the HTTP Basic auth.
  5. Choose the recently downloaded file.
  6. Do the next-next-finish magic.
  7. Voilá.
You might also need this as a reference: Configuring the Oracle Maven Repository

For the exact version of the archetype, take a ook inside: https://www.oracle.com/content/secure/maven/content/com/oracle/weblogic/archetype/basic-webapp/maven-metadata.xml

2017-01-10

Java: About returning Optional and Collections

Copy-paste post follows.
Of course, people will do what they want. But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe or Some type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter.

I think routinely using it as a return value for getters would definitely be over-use.

There's nothing wrong with Optional that it should be avoided, it's just not what many people wish it were, and accordingly we were fairly concerned about the risk of zealous over-use.

(Public service announcement: NEVER call Optional.get unless you can prove it will never be null; instead use one of the safe methods like orElse or ifPresent. In retrospect, we should have called get something like getOrElseThrowNoSuchElementException or something that made it far clearer that this was a highly dangerous method that undermined the whole purpose of Optional in the first place. Lesson learned.)

Source: http://stackoverflow.com/a/26328555/357403

The Lord has been spoken.

2016-12-15

Issue in Eclipse with Generated Sources

So I had a maven project in eclipse, which has a few grammars, generated by ANTLR. Once in a while, eclipse goes nuts, it fails to recognize the classes in the target/generated-sources directory.

What happened to me is that it somehow reinstated a filter in the Java Build Path. Something "reverts" the .classpath file in the project root...
  1. Right click on your project,
  2. click Properties,
  3. click on Java Build Path,
  4. find your source folder with the generated sources, and you will see Excluded: ** [means exclude all]
  5. click on it,
  6. click Edit,
  7. click on the Exclusion pattern stating **,
  8. click Remove,
  9. click Finish,
  10. now you should see Excluded: (None),
  11. click Apply,
  12. click OK,
  13. rebuild :)

2016-10-25

Java: About invoke bytecode instructions

You can find the Java bytecode instruction here.
  1. invokestatic: This instruction is used to call static methods on a class. It is determined at compile time, which method will be called.
  2. invokevirtual: This calls public and protected instance methods. Of course, it needs the instance reference on the stack, and also does the runtime binding (which adds overhead compared to invokestatic, but this "overhead" is not something you want to micro-optimize by design) for method dispatching. In fact, invokevirtual is not a true instruction, the actual steps taken are determined at runtime (cf. invokestatic) by the JVM, and can even involve Reflection, maybe even prohibit JIT in-place optimizations. Note, that the method handles are determined compile time, just the call path and strategy is determined at runtime (see: polymorphism).
  3. invokeinterface: Similar to invokevirtual, just the method dispatch is based on an interface, not a class.
  4. invokespecial: Calls the constructors (as they have special names that can't be referenced from plain Java code directly [on purpose]), also private and super (class parent's) methods.
  5. invokedynamic: This was introduced to address the "performance issues" of invokevirtual in a way, that the first call is executed as an invokevirtual, and the selected call path is stored for that invocation for subsequent reuse. In fact, this dynamic method resolution works with method handles obtained runtime (see: java.lang.invoke.* methods), not at compile time.
How does this affect you? Make your methods static if possible, but don't be overzealous! That's never a good approach.

2016-10-14

Java 8: Glossary of a session

OCP
Open/Closed Principle
Open for extension, Closed for modification Principle
SAM / Function Object
Only one method in a class. See: @FunctionalInterface; eg. Callable or Runnable.
Covariant Overriding
During inheritance, an overridden method's return type can be changed to one of it's subtypes. Eg. when overriding clone().
Erasure
Type parameters are removed from the types in the class bytecode.

2016-10-13

Java: PMD AvoidStringBufferField resolution

Inside an inherited code, I got a warning from PMD, stating
StringBuffers can grow quite a lot, and so may become a source of memory leak (if the owning class has a long life time).

StringBuffers/StringBuilders can grow considerably, and so may become a source of memory leaks if held within objects with long lifetimes.
So I went ahead and replaced the StringBuilder with a StringWriter. It even has the same append method signature for strings :D

2016-09-29

Java 8: Find First Match

Oh, how I love chainable methods, especially over a stream...

// Return first match in this List, or null.
return contents.stream().filter(c -> c.name.equals(fieldName)).findFirst().orElse(null);

<3 lambda <3