How to specify a ReadConcern to use with Spring's MongoTemplate? - java

With the vanilla java MongoClient you can specify a read concern for a particular query for example like this:
var result = mongoClient.getDatabase("somedb")
.getCollection("collection")
.withReadConcern(ReadConcern.MAJORITY)
.find(..)
But I cannot seem to find a way to set the ReadConcern using Spring's MongoTemplate. Preferably I'd like to set it for a specific query, but if this is not possible I'm fine with configuring something equivalent to a WriteConcernResolver (but for read concerns).
Is this possible, and if so how?
I'm using spring-data-mongodb version 3.2.6.

It seems that you maybe able to set this on the TransactionOptions and MongoTransactionManager:
https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/MongoTransactionManager.html
Example:
TransactionOptions transactionOptions = TransactionOptions.builder().readConcern(ReadConcern.LOCAL).writeConcern(WriteConcern.W1).build();
return new MongoTransactionManager(dbFactory.getMongoDatabaseFactory(), transactionOptions);
=== Edited ===
How about we take it the MongoClient route, since you stated it works on it? We could create a MongoTemplate from a given MongoClient instance:
Example:
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.MongoTemplate;
#Configuration
public class DemoApplication extends AbstractMongoClientConfiguration {
#Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), "someDbName");
}
#Override
public MongoClient mongoClient() {
final ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/someDbName");
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.readConcern(ReadConcern.DEFAULT)
.writeConcern(WriteConcern.MAJORITY)
.readPreference(ReadPreference.primary()).build();
return MongoClients.create(mongoClientSettings);
}
#Override
protected String getDatabaseName() {
return "someDbName";
}
}

Related

There is unknown field "container" in Tekton

I'm interested in Tekton these days.
However there are some issue when I implement Task with java fabric8.tekton apis.
There exist api which is adding steps in spec in units of container(withContainer) in TaskBuilder class.
However I got error message in rune time like below,
Can I get some advices?
Tekton version - v0.10.1
I used packages like below:
io.fabric8:kubernetes-client:4.7.1
io.fabric8:tekton-client:4.7.1
Here is my complete test code.
package com.example.tekton;
import java.util.ArrayList;
import java.util.List;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.client.BaseClient;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.tekton.client.TektonClient;
import io.fabric8.tekton.client.DefaultTektonClient;
import io.fabric8.tekton.client.handlers.TaskHandler;
import io.fabric8.tekton.client.handlers.TaskRunHandler;
import io.fabric8.tekton.pipeline.v1alpha1.ArrayOrString;
import io.fabric8.tekton.pipeline.v1alpha1.Task;
import io.fabric8.tekton.pipeline.v1alpha1.TaskBuilder;
import io.fabric8.tekton.pipeline.v1alpha1.TaskRun;
import io.fabric8.tekton.pipeline.v1alpha1.TaskRunBuilder;
import io.fabric8.tekton.pipeline.v1alpha1.TaskRefBuilder;
public class DefaultKubernetesTest {
public Task getTask() {
Container con = new ContainerBuilder()
.withNewImage("ubuntu")
.withNewName("echo-hello-world")
.addNewCommand("echo")
.addNewArg("hello jinwon world")
.build();
Task task = new TaskBuilder()
.withApiVersion("tekton.dev/v1alpha1")
.withKind("Task")
.withNewMetadata()
.withName("echo-hello-world-test")
.endMetadata()
.withNewSpec()
.addNewStep()
.withContainer(con)
.endStep()
.endSpec()
.build();
return task;
}
public TaskRun getTaskRun() {
TaskRun taskRun = new TaskRunBuilder()
.withNewMetadata()
.withName("taskrun")
.endMetadata()
.withNewSpec()
.withTaskRef(new TaskRefBuilder().withName("echo-hello-world-test").withApiVersion("tekton.dev/v1alpha1").withKind("Task").build())
.endSpec().build();
return taskRun;
}
public static void main(String[] args) {
ConfigBuilder config = new ConfigBuilder();
DefaultKubernetesTest kubeTest = new DefaultKubernetesTest();
String username = "testUser";
String password = "testPwd";
config = config.withMasterUrl("https://192.168.6.236:6443");
config = config.withUsername(username);
config = config.withPassword(password);
Config kubeConfig = config.build();
try (DefaultTektonClient test = new DefaultTektonClient(kubeConfig)) {
Task task = kubeTest.getTask();
TaskRun taskRun = kubeTest.getTaskRun();
test.tasks().inNamespace("test").create(task);
test.taskRuns().inNamespace("test").create(taskRun);
test.close();
}
}
}
Tekton ships with an admission controller, which validates the CRD specs before allowing them into the cluster. Because the project is still in alpha, its moving quite fast. Fabric8 may be templating out K8s objects against a different spec from what has been installed on your cluster. You should be able to validate the spec version used in Fabric8 and remove all the Tekton objects in your cluster and re-apply them at a specific version.

#Value returning null in unit test

I have a spring boot app with an Endpoint Test Configuration class and a unit test to test my http client. I am trying to get my server address and port from my application.properties which is located in my src/test.(All the classes are in my src/test.)
Here is my config class code :
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import javax.xml.bind.JAXBException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import com.nulogix.billing.service.PredictionEngineService;
import com.nulogix.billing.ws.endpoint.AnalyzeEndPoint;
import com.nulogix.billing.ws.endpoint.GetVersionEndPoint;
#Configuration
public class EndPointTestConfiguration {
#Value("${billing.engine.address}")
private String mockAddress;
#Value("${billing.engine.port}")
private String mockPort;
#Bean
public String getAddress() {
String serverAddress = "http://" + mockAddress + ":" + mockPort;
return serverAddress;
}
#Bean
public GetVersionEndPoint getVersionEndPoint() {
return new GetVersionEndPoint();
}
I annotated the values from my .properties with #value and then created a method that I instantiated with a bean to to return my server address string.
I then use that string value here in my HttpClientTest class:
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Map;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import com.google.gson.Gson;
import com.nulogix.billing.configuration.EndPointTestConfiguration;
import com.nulogix.billing.mockserver.MockServerApp;
#SpringBootTest(classes = EndPointTestConfiguration.class)
public class HttpClientTest {
#Autowired
EndPointTestConfiguration endpoint;
public static final String request_bad = "ncs|56-2629193|1972-03-28|20190218|77067|6208|3209440|self|";
public static final String request_good = "ncs|56-2629193|1972-03-28|20190218|77067|6208|3209440|self|-123|-123|-123|0.0|0.0|0.0|0.0|0.0|0.0|0.0";
//gets application context
static ConfigurableApplicationContext context;
//call mock server before class
#BeforeClass
static public void setup(){
SpringApplication springApplication = new SpringApplicationBuilder()
.sources(MockServerApp.class)
.build();
context = springApplication.run();
}
//shutdown mock server after class
#AfterClass
static public void tearDown(){
SpringApplication.exit(context);
}
#Test
public void test_bad() throws ClientProtocolException, IOException {
// missing parameter
String result = Request.Post(endpoint.getAddress())
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(request_bad, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
Map<?, ?> resultJsonObj = new Gson().fromJson(result, Map.class);
// ensure the key exists
assertEquals(resultJsonObj.containsKey("status"), true);
assertEquals(resultJsonObj.containsKey("errorMessage"), true);
// validate values
Boolean status = (Boolean) resultJsonObj.get("status");
assertEquals(status, false);
String errorMessage = (String) resultJsonObj.get("errorMessage");
assertEquals(errorMessage.contains("Payload has incorrect amount of parts"), true);
}
#Test
public void test_good() throws ClientProtocolException, IOException {
String result = Request.Post(endpoint.getAddress())
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(request_good, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
Map<?, ?> resultJsonObj = new Gson().fromJson(result, Map.class);
// ensure the key exists
assertEquals(resultJsonObj.containsKey("status"), true);
assertEquals(resultJsonObj.containsKey("errorMessage"), false);
assertEquals(resultJsonObj.containsKey("HasCopay"), true);
assertEquals(resultJsonObj.containsKey("CopayAmount"), true);
assertEquals(resultJsonObj.containsKey("HasCoinsurance"), true);
assertEquals(resultJsonObj.containsKey("CoinsuranceAmount"), true);
assertEquals(resultJsonObj.containsKey("version"), true);
// validate values
Boolean status = (Boolean) resultJsonObj.get("status");
assertEquals(status, true);
String version = (String) resultJsonObj.get("version");
assertEquals(version, "0.97");
}
}
I use it in the request.post, I didn't want to hardcode in my IP address and port number.
When I run the test it says
[ERROR] HttpClientTest.test_bad:63 NullPointer
[ERROR] HttpClientTest.test_good:86 NullPointer
But I am not sure why it is null? I am pretty sure I have everything instantiated and the string is clearly populated..
My package structure for my config is com.billing.mockserver and my package structure for my unit test is com.billing.ws.endpoint.
Here is my application.properties
server.port=9119
server.ssl.enabled=false
logging.config=classpath:logback-spring.xml
logging.file=messages
logging.file.max-size=50MB
logging.level.com.nulogix=DEBUG
billing.engine.address=127.0.0.1
billing.engine.port=9119
billing.engine.api.version=0.97
billing.engine.core.name=Patient_Responsibility
You are missing springboot basic understanding. #Configuration class is to initialize other spring beans and other things and are the first classes which get initialized. You should not #Autowire #configuration class.
In your Configuration class you can either create Spring bean for username and password and autowire that in your test class or directly use #Value in your Test class.
Example: in your configuration class you are creating bean of GetVersionEndPoint and you can autowire that in your Test class.
Update 2:
For test classes, you need to add application.properties file in src\test\resource

NoSuchMethod when calling AWS Lambda using the AWS Sdk

I've created & deployed one simple GET API in API Gateway and here is the ARN and there is no authentication whatsoever on this function, I can simply call it on my browser
arn:aws:lambda:ap-southeast-1:XXXXXXXXXXXXXX:function:La
and the public url that can be browsed using the browser is:
https://xxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/v1/lambda/geta
and I'm using Spring boot project and the below code to invoke the API (Following this Doc)
The interface as the lambda service
package com.xxxxxxx.services.interfaces;
import com.amazonaws.services.lambda.invoke.LambdaFunction;
public interface ILambdaGetBalance {
#LambdaFunction(functionName="La")
String getA();
}
The service using that interface to call the lambda function
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.xxxxxxxx.services.interfaces.ILambdaGetBalance;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
#Service
public class LambdaService {
#Value("${aws.access-key}")
private String accessKey;
#Value("${aws.secret-key}")
private String secretKey;
#Value("${aws.lambda.region-name}") // this is ap-southeast-1
private String regionName;
public void test() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey,
secretKey);
AWSLambda client = AWSLambdaClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(regionName)
.build();
final ILambdaGetBalance getBalance = LambdaInvokerFactory.builder()
.lambdaClient(client)
.build(ILambdaGetBalance.class);
getBalance.getA();
}
}
after calling the getA function the system will through the following exception:
java.lang.NoSuchMethodError: com.amazonaws.services.lambda.AWSLambdaClient.beforeClientExecution(Lcom/amazonaws/AmazonWebServiceRequest;)Lcom/amazonaws/AmazonWebServiceRequest;
Any idea why is this happening? What am I missing?
Looks like your aws-java-sdk-lambda and aws-java-sdk-core modules may have incompatible versions. How are you resolving the dependencies for your project? The beforeClientExecution method was added to the AmazonWebServiceClient base class in version 1.11.106 of aws-java-sdk-core - see here: https://github.com/aws/aws-sdk-java/blame/master/aws-java-sdk-core/src/main/java/com/amazonaws/AmazonWebServiceClient.java#L590

dropwizard hibernate use a resource from within the server

I'm new to dropwizard and i'm trying to create an Authenticator which gets credentials from the user, then it uses the rest api getUser method which i implemented in my UserResouce class to get the user with the username that is in the credentials from the db users table. However in my autheticator class i having troubles in figuring out how to use the user resource functions to get the user.
I was trying to do something like that:
public List<com.amitbaz.tss.db.User> getUsersFromDB(String username){
SessionFactory sessionFactory = TradingSystemServerApplication.hibernateBundle.getSessionFactory();
UserDAO userDAO = new UserDAO(sessionFactory);
List<com.amitbaz.tss.db.User> user = userDAO.getUser(username);
logger.debug(user.toString());
return user;
}
inside the autheticator and call it from the authenticte function but it says there is no session bound...
EDIT:
Ok so after much thinking i got to this:
I'm dropwizard authenticator and authorizer implementions with BasicCredentials and.
Autheticator (don't mind the VALID_USER thing..):
package com.amitbaz.tss.auth;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.persistence.NamedQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.TradingSystemServerApplication;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class TradingSystemServerAuthenticator implements Authenticator<BasicCredentials, User> {
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthenticator.class);
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"amit", ImmutableSet.of("admin"),
"stav", ImmutableSet.of("broker")
);
private UserDAO userDAO;
public TradingSystemServerAuthenticator(UserDAO userDAO) {
// TODO Auto-generated constructor stub
this.userDAO = userDAO;
}
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
// TODO Auto-generated method stub
List<com.amitbaz.tss.db.User> user = userDAO.getUser(credentials.getUsername());
logger.debug(user.toString());
if("amit".equals(credentials.getPassword())){
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
Authorizer:
package com.amitbaz.tss.auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.db.UserDAO;
import io.dropwizard.auth.Authorizer;
public class TradingSystemServerAuthorizer implements Authorizer<User>{
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthorizer.class);
private UserDAO userDAO;
public TradingSystemServerAuthorizer(UserDAO userDAO) {
super();
this.userDAO = userDAO;
}
#Override
public boolean authorize(User user, String role) {
// TODO Auto-generated method stub
logger.debug(userDAO.getUser(user.getName()).toString());
return user.getName().equals("amit") && user.getRole().contains(new String("admin"));
}
}
Now, In my Application class I do this:
package com.amitbaz.tss;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new
env.jersey().register(new UserResource(userDAO));
/...
BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new TradingSystemServerAuthenticator(userDAO))
.setAuthorizer(new TradingSystemServerAuthorizer(userDAO))
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
}
And I have the annotation #RolesAllowed("role_name") on one of the rest api methods which with im trying to test the auth.
Now when i try to test this and i make a request to that rest api method, I
get the error No session currently bound to execution context where i do userDAO.getUser(...) in the authanticator and in the authorizer
EDIT 2:
UserDAO implementation:
package com.amitbaz.tss.db;
import java.util.List;
import org.hibernate.SessionFactory;
import io.dropwizard.hibernate.AbstractDAO;
public class UserDAO extends AbstractDAO<User>{
public UserDAO(SessionFactory sessionFactory) {
super(sessionFactory);
// TODO Auto-generated constructor stub
}
public List<User> getUser(String username){
return list(namedQuery("com.amitbaz.tss.db.user.getUser")
.setParameter("username", username));
}
}
EDIT 3:
Added #UnitOfWork to authenticate and authorize methods.
registered them as follow ( Notice the changes in hibernateBundle and run method):
package com.amitbaz.tss;
import javax.servlet.ServletRegistration;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereServlet;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
,TradingSystemServerAuthenticator.class, TradingSystemServerAuthorizer.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new UserRoleDAO(hibernateBundle.getSessionFactory());
final TradingSystemServerAuthorizer authorizer = new TradingSystemServerAuthorizer(userDAO);
final TradingSystemServerAuthenticator authenticator = new TradingSystemServerAuthenticator(userDAO);
env.jersey().register(new UserResource(userDAO));
env.jersey().register(new UserRoleResource(userRoleDAO));
env.jersey().register(authorizer);
env.jersey().register(authenticator);
env.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(authenticator)
.setAuthorizer(authorizer)
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Your approach looks like a design issue. The issue I see is that you are trying to integrate via rest with a service that is already accessible for you within your application. That adds a lot of overhead and complicates things.
Fortunately, DW already has a fully integrated Authorization and Authentication system just waiting for you to plug in. You can read more about it here: http://www.dropwizard.io/1.0.2/docs/manual/auth.html
The essential thing to note here is that you should split the service used by your resource from your resource. In your case for example the UserDao, or you could split it into a UserService and UserResource, where the UserService provides access to your database layer. Up to you really.
Here is how you would implement this with DW integrated auth and how you would register this as well.
In my example I am skipping the Hibernate aspect of this as it isn't too relevant. you can read about it here: http://www.dropwizard.io/1.0.2/docs/manual/hibernate.html
Here's my code:
public class AuthenticatorTest extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
// register resource
environment.jersey().register(MyHelloResource.class);
// create the dao + dependencies
UserDao dao = new UserDao(null);
// register new authenticator
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(new UserAuth(dao)).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
// enables authentication via filter
environment.jersey().register(RolesAllowedDynamicFeature.class);
}
public static void main(String[] args) throws Exception {
new AuthenticatorTest().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class MyHelloResource {
#GET
#Path("asd")
#PermitAll
public String test(String x) {
return "Hello";
}
}
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
public static class UserDao {
private SessionFactory s;
public UserDao(final SessionFactory s) {
this.s = s;
}
public String getUser() {
return "pandaadb";
}
}
}
And this is the breakdown of what we are doing.
First, as per docs, you would register your HibernateBundle within the bootstrap method as shown (in docs). This gives you access to the SessionFactory you require for your authentication.
Your resource method will be annotated with a java security annotation. i am using PermitAll because I am disregarding roles.
In the run method, you then create your DAO, register your resource and use the DW builder to add the required Filter and the Authenticator. This one specifically is for BasicCredentials, however there is nothing stopping you from doing any kind of filter for this. DW already supports things like Ldap (in a different dependency), Basic Auth and so on.
Now, since you create your beans in the run method, and you added your Hibernate bundle in the bootstrap method, you have access to the SessionFactory and can instantiate the DAO accordingly. No need to have to pass it around.
You also don't have to do any rest-request to access your user (though there is nothing stopping you adding that resource anyway, in case you need external access to it.)
So, to sum up, the important parts are:
Add A security annotation to your resource (e.g. PermitAll to allow all roles)
Add an authenticator implementation (in my case UserAuth) that uses your DAO
Instantiate it in the run method provided by dropwizard and register it with the jersey environment.
Note, this requires your user to implement the javax.security.Principal interface. this is not a bad idea in general as a lot of security Frameworks make use of this.
This, also, gives you more options with regards to DW.
You can add an Authorization implementation and a filter, and you'll be able to inject the User object into any resource method by adding an #Auth annotated object (see docs).
Finally, the test of the standalone app from above:
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 17 Oct 2016 10:45:51 GMT
< WWW-Authenticate: Basic realm="SUPER SECRET STUFF"
< Content-Type: text/plain
< Content-Length: 49
<
* Connection #0 to host localhost left intact
Credentials are required to access this resource.
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -H "Authorization: Basic dXNlcjpwYXNz" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Basic dXNlcjpwYXNz
>
< HTTP/1.1 200 OK
< Date: Mon, 17 Oct 2016 10:46:11 GMT
< Content-Type: application/json
< Vary: Accept-Encoding
< Content-Length: 5
<
* Connection #0 to host localhost left intact
Helloartur
I hope this helps you with your issue.
Of course you don't need to use the DW method for Authentication. However I would recommend going down that road as you will have more support and a lot of things out of the box.
However, the one thing you should rethink (if you don't use DW) is to not do a curl request to your Filter. Instead, instantiate it in the run method, and pass that instance to your Filter.
Note also, if you register your DAO with DW (as seen in hibernate docs), you will be able to use #Inject to inject your DAO into the Filter class that needs to use it.
Right, I think that's all the info you need :)
Let me know if you have any problems,
Artur
Edit:
I am doing an edit because I wrote a lot above and don't want to go over it.
I set up Hibernate to test this. The reason you are seeing issues is because the UnitOfWork is bound to the request scope. However, the resource annotations is matched AFTER the filter is invoked (since you need to do auth before executing the method). This is why you don't have a session.
This is the solution.
In your run method, register a proxy for your auth implementation:
UnitOfWorkAwareProxyFactory fac = new UnitOfWorkAwareProxyFactory(hibernate);
UserAuth proxy = fac.create(UserAuth.class, UserDao.class, dao);
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(proxy).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
This creates a proxy around the UserAuth class so that it is aware of the UnitOfWork annotation.
In your UserAuth class (or mine rather) you do:
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
#UnitOfWork
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
Note the UnitOfWork annotation on the authenticate. This now opens a new session for you. Please make sure to read up on UnitOfWork as it may have tricky side effects (or not) depending on how you use it.
Finally, this allowed my dao to talk to the database on an existing session.
Regards,
Artur
I finally, after much debugginf, find the proper way to open a session and execute a query.
I added this lines in Authenticator's authenticate mehod:
Session session = userDAO.getSessionFactory().openSession();
Transaction transaction = session.getTransaction();
Query userquery = session.createQuery("select u from User u where u.username = :username").setParameter("username", credentials.getUsername());
List<com.amitbaz.tss.db.User> u = userquery.list();
session.close();
And the same in Authorizater and it now works :)

Read from database and write to file using camel

I want to read from the database and write the records to a file using Camel. Below is my code:
import javax.sql.DataSource;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.SimpleRegistry;
import org.apache.commons.dbcp.BasicDataSource;
public class JDBCExampleSimpleRegistry {
public static void main(String[] args) throws Exception {
final String url = "jdbc:oracle:thin:#MYSERVER:1521:myDB";
DataSource dataSource = setupDataSource(url);
SimpleRegistry reg = new SimpleRegistry() ;
reg.put("myDataSource",dataSource);
CamelContext context = new DefaultCamelContext(reg);
context.addRoutes(new JDBCExampleSimpleRegistry().new MyRouteBuilder());
context.start();
Thread.sleep(5000);
context.stop();
}
class MyRouteBuilder extends RouteBuilder {
public void configure() {
String dst = "C:/Local Disk E/TestData/Destination/?fileName=output.txt";
from("direct:myTable")
.setBody(constant("select * from myTable"))
.to("jdbc:myDataSource")
.to("file://" + dst);
}
}
private static DataSource setupDataSource(String connectURI) {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setUsername("sa");
ds.setPassword("devon1");
ds.setUrl(connectURI);
return ds;
}
}
The above program works fine and CamelContext is elegantly shutdown. However, the target file is not created. What am I doing wrong?
I am a newbie to Apache Camel so appreciate any help. I am using JDK7 with Apache Camel 2.12.1 and is not using Spring.
You can take a look at the SQL example: http://camel.apache.org/sql-example.html and then to write to file, is just to send to a file instead of calling the bean as the sql example does.
If you want to use the JDBC component then it doesnt have built-in consumer, so you need to trigger the route using a timer or a quartz scheduler to run the route every X time.

Categories

Resources