I have a small Micronaut application with a view layer (thymeleaf). Now I want to integrate Google Analytics or some other tracking tool. Of course this should be just loaded in production mode but how can I do a check for the environment in the view layer?
I start the application in the following way:
java -Dmicronaut.environments=prod -jar mywebsite.jar
You can inject the io.micronaut.context.env.Environment and pass the result of getActiveNames() as part of your view model
EDIT:
If you want to add this data to each model without touching each controller method you can add a server filter that happens before the view filter to manipulate the response, or create a piece of around advice that can manipulate the return value of the controller.
Another approach to solving this problem is to utilise #Replaces annotation to override ThymeleafViewsRenderer class and add common data in your class.
import io.micronaut.context.annotation.Replaces
import io.micronaut.context.env.Environment
import io.micronaut.core.beans.BeanMap
import io.micronaut.core.io.Writable
import io.micronaut.core.io.scan.ClassPathResourceLoader
import io.micronaut.core.util.ArgumentUtils
import io.micronaut.http.HttpRequest
import io.micronaut.views.exceptions.ViewRenderingException
import io.micronaut.views.thymeleaf.ThymeleafViewsRenderer
import io.micronaut.views.thymeleaf.WebContext
import org.thymeleaf.TemplateEngine
import org.thymeleaf.context.Context
import org.thymeleaf.context.IContext
import org.thymeleaf.exceptions.TemplateEngineException
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver
import javax.annotation.Nonnull
import javax.annotation.Nullable
import javax.inject.Inject
import javax.inject.Singleton
#Singleton
#Replaces(bean = ThymeleafViewsRenderer.class)
class CustomThymeleafFilter extends ThymeleafViewsRenderer {
CustomThymeleafFilter(AbstractConfigurableTemplateResolver templateResolver, TemplateEngine templateEngine, ClassPathResourceLoader resourceLoader) {
super(templateResolver, templateEngine, resourceLoader)
}
#Inject
Environment environment
#Override
#Nonnull
public Writable render(#Nonnull String viewName, #Nullable Object data) {
ArgumentUtils.requireNonNull("viewName", viewName);
return (writer) -> {
//following block adds environments variable to model
Map dataMap = variables(data)
dataMap.put("environment", environment.activeNames)
IContext context = new Context(Locale.US, dataMap);
processView(viewName, writer, context);
};
}
#Override
#Nonnull
public Writable render(#Nonnull String viewName, #Nullable Object data,
#Nonnull HttpRequest<?> request) {
ArgumentUtils.requireNonNull("viewName", viewName);
ArgumentUtils.requireNonNull("request", request);
return (writer) -> {
//following block adds environments variable to model
Map dataMap = variables(data)
dataMap.put("environment", environment.activeNames)
IContext context = new WebContext(request, Locale.US, dataMap);
processView(viewName, writer, context);
};
}
private static Map<String, Object> variables(#Nullable Object data) {
if (data == null) {
return new HashMap<>();
}
if (data instanceof Map) {
return (Map<String, Object>) data;
} else {
return BeanMap.of(data);
}
}
private void processView(String viewName, Writer writer, IContext context) {
try {
engine.process(viewName, context, writer);
} catch (TemplateEngineException e) {
throw new ViewRenderingException("Error rendering Thymeleaf view [" + viewName + "]: " + e.getMessage(), e);
}
}
}
Related
I'm trying to rewrite some legacy code which used org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore to store the access tokens. I'm currently trying to use the RedisTokenStore instead of the previously used InMemoryTokenStore. The token gets generated and gets stored in Redis fine (Standalone redis configuration), however, deserialization of OAuth2Authentication fails with the following error:
Could not read JSON: Cannot construct instance of `org.springframework.security.oauth2.provider.OAuth2Authentication` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Since there's no default constructor for this class, the deserialization and mapping to the actual object while looking up from Redis fails.
RedisTokenStore redisTokenStore = new RedisTokenStore(jedisConnectionFactory);
redisTokenStore.setSerializationStrategy(new StandardStringSerializationStrategy() {
#Override
protected <T> T deserializeInternal(byte[] bytes, Class<T> aClass) {
return Utilities.parse(new String(bytes, StandardCharsets.UTF_8),aClass);
}
#Override
protected byte[] serializeInternal(Object o) {
return Objects.requireNonNull(Utilities.convert(o)).getBytes();
}
});
this.tokenStore = redisTokenStore;
public static <T> T parse(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (IOException e) {
log.error("Jackson2Json failed: " + e.getMessage());
} return null;}
public static String convert(Object data) {
try {
return OBJECT_MAPPER.writeValueAsString(data);
} catch (JsonProcessingException e) {
log.error("Conversion failed: " + e.getMessage());
}
return null;
}
How is OAuth2Authentication object reconstructed when the token is looked up from Redis? Since it does not define a default constructor, any Jackson based serializer and object mapper won't be able to deserialize it.
Again, the serialization works great (since OAuth2Authentication implements Serializable interface) and the token gets stored fine in Redis. It just fails when the /oauth/check_token is called.
What am I missing and how is this problem dealt with while storing access token in Redis?
I solved the issue by writing custom deserializer. It looks like this:
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import java.io.IOException;
public class AuthorizationGrantTypeCustomDeserializer extends JsonDeserializer<AuthorizationGrantType> {
#Override
public AuthorizationGrantType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
Root root = p.readValueAs(Root.class);
return root != null ? new AuthorizationGrantType(root.value) : new AuthorizationGrantType("");
}
private static class Root {
public String value;
}
public static SimpleModule generateModule() {
SimpleModule authGrantModule = new SimpleModule();
authGrantModule.addDeserializer(AuthorizationGrantType.class, new AuthorizationGrantTypeCustomDeserializer());
return authGrantModule;
}
}
Then I registered deserializer in objectMapper which is later used by jackson API
ObjectMapper mapper = new ObjectMapper()
.registerModule(AuthorizationGrantTypeCustomDeserializer.generateModule());
I was asking myself if there was a way to push the reactive context into a ThreadLocal variable before a subscriber received the onNext signal. While digging inside reactor-core, I've found Hooks class and Lift BiFunction.
I've created a class with the following implementation. The class is composed of a ThreadLocal variable that will hold the Context and implements the necessary BiFunction interface. It will delegate all the call to the actual subscriber and will also push the context if modified into the ThreadLocal variable before calling the onNext on the actual subscriber.
package com.example.demo;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.CoreSubscriber;
import reactor.core.Scannable;
import reactor.util.context.Context;
import java.util.function.BiFunction;
public class ThreadLocalContextLifter<T> implements BiFunction<Scannable, CoreSubscriber<? super T>, CoreSubscriber<? super T>> {
private static Logger logger = LoggerFactory.getLogger(ThreadLocalContextLifter.class);
private static final ThreadLocal<Context> contextHolder = new ThreadLocal<>();
public static Context getContext() {
Context context = contextHolder.get();
if (context == null) {
context = Context.empty();
contextHolder.set(context);
}
return context;
}
public static void setContext(Context context) {
contextHolder.set(context);
}
#Override
public CoreSubscriber<? super T> apply(Scannable scannable, CoreSubscriber<? super T> coreSubscriber) {
return new ThreadLocalContextCoreSubscriber<>(coreSubscriber);
}
final class ThreadLocalContextCoreSubscriber<U> implements CoreSubscriber<U> {
private CoreSubscriber<? super U> delegate;
public ThreadLocalContextCoreSubscriber(CoreSubscriber<? super U> delegate) {
this.delegate = delegate;
}
#Override
public Context currentContext() {
return delegate.currentContext();
}
#Override
public void onSubscribe(Subscription s) {
delegate.onSubscribe(s);
}
#Override
public void onNext(U u) {
Context context = delegate.currentContext();
if (!context.isEmpty()) {
Context currentContext = ThreadLocalContextLifter.getContext();
if (!currentContext.equals(context)) {
logger.info("Pushing reactive context to holder {}", context);
ThreadLocalContextLifter.setContext(context);
}
}
delegate.onNext(u);
}
#Override
public void onError(Throwable t) {
delegate.onError(t);
}
#Override
public void onComplete() {
delegate.onComplete();
}
}
}
The instance is loaded into the Hooks with the following code:
Hooks.onEachOperator(Operators.lift(new ThreadLocalContextLifter<>()));
I've run some tests and it seems to work properly but I'm not convinced by the solution. I'm guessing that the hook will degrade the performance of reactor or that it will not work in some case that I'm not aware of.
My question is simple: Is this a bad idea?
I don't think there is anything wrong with that idea... The hook is used by every Reactor-provided operator.
The Context doesn't change between onNext, so the lift ThreadLocalContextCoreSubscriber could capture it in onSubscribe. But you'd still need to check the ThreadLocal at least once in onNext, since onNext and onSubscribe can happen on two different threads, so your solution of using delegate.currentContext() works too. In the end, your approach looks sound.
Is it the correct way to create a in memory db using neo4j ? So that traverse query will hit only cache and not the disk .
approach - 1 : I tried with this :
package com.test;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class CreateDBFactory {
private static GraphDatabaseService graphDb = null;
public static final String DB = "test/db";
public static GraphDatabaseService createInMemoryDB() {
System.out.println("- Inside createInMemoryDB() - ");
if (null == graphDb) {
synchronized (GraphDatabaseService.class) {
if (null == graphDb) {
System.out.println(" - Inside if clause -");
final Map<String, String> config = new HashMap<>();
config.put("neostore.nodestore.db.mapped_memory", "50M");
config.put("string_block_size", "60");
config.put("array_block_size", "300");
graphDb = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder(DB).setConfig(config)
.newGraphDatabase();
registerShutdownHook(graphDb);
}
}
}
return graphDb;
}
private static void registerShutdownHook(final GraphDatabaseService graphDb) {
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
graphDb.shutdown();
}
});
}
public static void clearDb() {
try {
if (graphDb != null) {
graphDb.shutdown();
graphDb = null;
}
FileUtils.deleteRecursively(new File(DB));
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}
Approach -2 : Using Neo4jBasicDocTest class.
Here new ImpermanentDatabaseBuilder() is not creating the target/test-data/impermanent-db folder. So not able to test " Nancy" node is created or not .
Neo4j doesn't have an 'in memory' mode in the sense that all data always stored in memory and no disk storage is used. ImpermanentGraphDatabase is the closest you'll find to anything like that, but that just creates a data directory at random and will delete it when it is shutdown.
If you are okay with using disk, you can use the above ImpermanentGraphDatabase, and just set the cache of neo4j to be really high. This will make everything be stored in memory, as well as on disk.
I used the HelloItemizedOverlay code from Google to create and overlay item and it works but now I need to make the image translucent and change the bounds and I do not know how that works exactly.
I found the method getOpacity() but I do not know how to implement it in my ItemizedOverlay class or in my Map class. I am also aware of getIntrinsicHeight() and getIntrinsicWidth() methods but like the getOpacity() I do not know how to use it correctly. The code I have used for my ItemizedOverlay is :
import java.util.ArrayList;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;
public class ContourItemizedOverlay extends ItemizedOverlay {
Context mContext;
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
public ContourItemizedOverlay(Drawable defaultMarker, Context context) {
super(boundCenter(defaultMarker));
mContext = context;
// TODO Auto-generated constructor stub
}
public int getOpacity()
{
return PixelFormat.TRANSPARENT;
}
#Override
protected boolean onTap(int index) {
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
public void addOverlay(OverlayItem overlay) {
mOverlays.add(overlay);
populate();
}
#Override
protected OverlayItem createItem(int i) {
// TODO Auto-generated method stub
return mOverlays.get(i);
}
#Override
public int size() {
// TODO Auto-generated method stub
return mOverlays.size();
}
}
I'm just getting started with Dropwizard 0.4.0, and I would like some help with HMAC authentication. Has anybody got any advice?
Thank you in advance.
At present Dropwizard doesn't support HMAC authentication right out of the box, so you'd have to write your own authenticator. A typical choice for HMAC authentication is to use the HTTP Authorization header. The following code expects this header in the following format:
Authorization: <algorithm> <apiKey> <digest>
An example would be
Authorization: HmacSHA1 abcd-efgh-1234 sdafkljlkansdaflk2354jlkj5345345dflkmsdf
The digest is built from the content of the body (marshalled entity) prior to URL encoding with the HMAC shared secret appended as base64. For a non-body request, such as GET or HEAD, the content is taken as the complete URI path and parameters with the secret key appended.
To implement this in a way that Dropwizard can work with it requires you to copy the BasicAuthenticator code present in the dropwizard-auth module into your own code and modify it with something like this:
import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.yammer.dropwizard.auth.AuthenticationException;
import com.yammer.dropwizard.auth.Authenticator;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
class HmacAuthInjectable<T> extends AbstractHttpContextInjectable<T> {
private static final String PREFIX = "HmacSHA1";
private static final String HEADER_VALUE = PREFIX + " realm=\"%s\"";
private final Authenticator<HmacCredentials, T> authenticator;
private final String realm;
private final boolean required;
HmacAuthInjectable(Authenticator<HmacCredentials, T> authenticator, String realm, boolean required) {
this.authenticator = authenticator;
this.realm = realm;
this.required = required;
}
public Authenticator<HmacCredentials, T> getAuthenticator() {
return authenticator;
}
public String getRealm() {
return realm;
}
public boolean isRequired() {
return required;
}
#Override
public T getValue(HttpContext c) {
try {
final String header = c.getRequest().getHeaderValue(HttpHeaders.AUTHORIZATION);
if (header != null) {
final String[] authTokens = header.split(" ");
if (authTokens.length != 3) {
// Malformed
HmacAuthProvider.LOG.debug("Error decoding credentials (length is {})", authTokens.length);
throw new WebApplicationException(Response.Status.BAD_REQUEST);
}
final String algorithm = authTokens[0];
final String apiKey = authTokens[1];
final String signature = authTokens[2];
final String contents;
// Determine which part of the request will be used for the content
final String method = c.getRequest().getMethod().toUpperCase();
if ("GET".equals(method) ||
"HEAD".equals(method) ||
"DELETE".equals(method)) {
// No entity so use the URI
contents = c.getRequest().getRequestUri().toString();
} else {
// Potentially have an entity (even in OPTIONS) so use that
contents = c.getRequest().getEntity(String.class);
}
final HmacCredentials credentials = new HmacCredentials(algorithm, apiKey, signature, contents);
final Optional<T> result = authenticator.authenticate(credentials);
if (result.isPresent()) {
return result.get();
}
}
} catch (IllegalArgumentException e) {
HmacAuthProvider.LOG.debug(e, "Error decoding credentials");
} catch (AuthenticationException e) {
HmacAuthProvider.LOG.warn(e, "Error authenticating credentials");
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
if (required) {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.AUTHORIZATION,
String.format(HEADER_VALUE, realm))
.entity("Credentials are required to access this resource.")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
return null;
}
}
The above is not perfect, but it'll get you started. You may want to refer to the MultiBit Merchant release candidate source code (MIT license) for a more up to date version and the various supporting classes.
The next step is to integrate the authentication process into your ResourceTest subclass. Unfortunately, Dropwizard doesn't provide a good entry point for authentication providers in v0.4.0, so you may want to introduce your own base class, similar to this:
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.test.framework.AppDescriptor;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.LowLevelAppDescriptor;
import com.xeiam.xchange.utils.CryptoUtils;
import com.yammer.dropwizard.bundles.JavaBundle;
import com.yammer.dropwizard.jersey.DropwizardResourceConfig;
import com.yammer.dropwizard.jersey.JacksonMessageBodyProvider;
import com.yammer.dropwizard.json.Json;
import org.codehaus.jackson.map.Module;
import org.junit.After;
import org.junit.Before;
import org.multibit.mbm.auth.hmac.HmacAuthProvider;
import org.multibit.mbm.auth.hmac.HmacAuthenticator;
import org.multibit.mbm.persistence.dao.UserDao;
import org.multibit.mbm.persistence.dto.User;
import org.multibit.mbm.persistence.dto.UserBuilder;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Set;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* A base test class for testing Dropwizard resources.
*/
public abstract class BaseResourceTest {
private final Set<Object> singletons = Sets.newHashSet();
private final Set<Object> providers = Sets.newHashSet();
private final List<Module> modules = Lists.newArrayList();
private JerseyTest test;
protected abstract void setUpResources() throws Exception;
protected void addResource(Object resource) {
singletons.add(resource);
}
public void addProvider(Object provider) {
providers.add(provider);
}
protected void addJacksonModule(Module module) {
modules.add(module);
}
protected Json getJson() {
return new Json();
}
protected Client client() {
return test.client();
}
#Before
public void setUpJersey() throws Exception {
setUpResources();
this.test = new JerseyTest() {
#Override
protected AppDescriptor configure() {
final DropwizardResourceConfig config = new DropwizardResourceConfig();
for (Object provider : JavaBundle.DEFAULT_PROVIDERS) { // sorry, Scala folks
config.getSingletons().add(provider);
}
for (Object provider : providers) {
config.getSingletons().add(provider);
}
Json json = getJson();
for (Module module : modules) {
json.registerModule(module);
}
config.getSingletons().add(new JacksonMessageBodyProvider(json));
config.getSingletons().addAll(singletons);
return new LowLevelAppDescriptor.Builder(config).build();
}
};
test.setUp();
}
#After
public void tearDownJersey() throws Exception {
if (test != null) {
test.tearDown();
}
}
/**
* #param contents The content to sign with the default HMAC process (POST body, GET resource path)
* #return
*/
protected String buildHmacAuthorization(String contents, String apiKey, String secretKey) throws UnsupportedEncodingException, GeneralSecurityException {
return String.format("HmacSHA1 %s %s",apiKey, CryptoUtils.computeSignature("HmacSHA1", contents, secretKey));
}
protected void setUpAuthenticator() {
User user = UserBuilder
.getInstance()
.setUUID("abc123")
.setSecretKey("def456")
.build();
//
UserDao userDao = mock(UserDao.class);
when(userDao.getUserByUUID("abc123")).thenReturn(user);
HmacAuthenticator authenticator = new HmacAuthenticator();
authenticator.setUserDao(userDao);
addProvider(new HmacAuthProvider<User>(authenticator, "REST"));
}
}
Again, the above code is not perfect, but the idea is to allow a mocked up UserDao to provide a standard user with a known shared secret key. You'd have to introduce your own UserBuilder implementation for testing purposes.
Finally, with the above code a Dropwizard Resource that had an endpoint like this:
import com.google.common.base.Optional;
import com.yammer.dropwizard.auth.Auth;
import com.yammer.metrics.annotation.Timed;
import org.multibit.mbm.core.Saying;
import org.multibit.mbm.persistence.dto.User;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.atomic.AtomicLong;
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
private final String template;
private final String defaultName;
private final AtomicLong counter;
public HelloWorldResource(String template, String defaultName) {
this.template = template;
this.defaultName = defaultName;
this.counter = new AtomicLong();
}
#GET
#Timed
#Path("/hello-world")
public Saying sayHello(#QueryParam("name") Optional<String> name) {
return new Saying(counter.incrementAndGet(),
String.format(template, name.or(defaultName)));
}
#GET
#Timed
#Path("/secret")
public Saying saySecuredHello(#Auth User user) {
return new Saying(counter.incrementAndGet(),
"You cracked the code!");
}
}
could be tested with a unit test that was configured like this:
import org.junit.Test;
import org.multibit.mbm.core.Saying;
import org.multibit.mbm.test.BaseResourceTest;
import javax.ws.rs.core.HttpHeaders;
import static org.junit.Assert.assertEquals;
public class HelloWorldResourceTest extends BaseResourceTest {
#Override
protected void setUpResources() {
addResource(new HelloWorldResource("Hello, %s!","Stranger"));
setUpAuthenticator();
}
#Test
public void simpleResourceTest() throws Exception {
Saying expectedSaying = new Saying(1,"Hello, Stranger!");
Saying actualSaying = client()
.resource("/hello-world")
.get(Saying.class);
assertEquals("GET hello-world returns a default",expectedSaying.getContent(),actualSaying.getContent());
}
#Test
public void hmacResourceTest() throws Exception {
String authorization = buildHmacAuthorization("/secret", "abc123", "def456");
Saying actual = client()
.resource("/secret")
.header(HttpHeaders.AUTHORIZATION, authorization)
.get(Saying.class);
assertEquals("GET secret returns unauthorized","You cracked the code!", actual.getContent());
}
}
Hope this helps you get started.