If I use the input in an events rule.. how am I supposed parse it in the lambda?
Now I have:
MyJobScheduledRule:
Type: AWS::Events::Rule
Properties:
Description: Scheduled Rule
ScheduleExpression: !Sub "rate(${IntervalMinutes} minutes)"
State: "ENABLED"
Targets:
- Id: "MyJobLambda"
Arn: !GetAtt MyJobLambda.Arn
Input: "\"{\\\"key\\\":\\\"value\\\"}\"
With the following lambda:
public class MyJobLambda implements RequestHandler<Map<String, String>, Void> {
private static Logger LOGGER = LoggerFactory.getLogger(MyJobLambda.class);
#Override
public Void handleRequest(Map<String, String> event, Context context) {
LOGGER.debug("MyJob got value {} from input", event.get("key"));
return null;
}
}
But I get the following runtime exception:
An error occurred during JSON parsing: java.lang.RuntimeException
java.lang.RuntimeException: An error occurred during JSON parsing
Caused by: java.io.UncheckedIOException: com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.LinkedHashMap` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('
{
"key": "value"
}
I have also tried using a POJO as input to the lambda. Any ideas?
Use a ScheduledEvent object, and parse the detail field of the event
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
public class MyJobLambda implements RequestHandler<Map<String, String>, Void> {
private static Logger LOGGER = LoggerFactory.getLogger(MyJobLambda.class);
#Override
public Void handleRequest(ScheduledEvent event, Context context) {
LOGGER.debug("MyJob got value {} from input", mapScheduledEventDetail(event.getDetail());
return null;
}
private String mapScheduledEventDetail(Map<String,Object> detailObject) {
return ParserUtil.parseObjectToJson(detailObject.get("clientId"));
}
}
Related
I've following stack which deploys the two constructs within. The construct DynamoDBConstruct exports the table name, and the construct IAMRoleConstruct consumes it. However, during deployment, it fails stating No export named dbTableName found, despite the fact that dependency is added/specified, the IAMRoleConstruct gets deployed first, why?
Stack:
public AllStacks(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var db = new DynamoDBConstruct(this, "DynamoDB");
var iam = new IAMRoleConstruct(this, "IAMRole");
iam.Node.AddDependency(db);
}
DynamoDBConstruct
public DynamoDBConstruct(Construct scope, string id): base(scope, id)
{
var dbTable = new Table(this, "dbTable", new TableProps()
{
PartitionKey = new Attribute
{
Name = "contactID",
Type = AttributeType.STRING
},
TableClass = TableClass.STANDARD,
TableName = (string)Node.TryGetContext("dbTableName"),
RemovalPolicy = RemovalPolicy.DESTROY
});
new CfnOutput(this, "OutputTableName", new CfnOutputProps()
{
ExportName = "dbTableName",
Value = dbTable.TableName
});
}
IAMRoleConstruct
public IAMRoleConstruct(Construct scope, string id) : base(scope, id)
{
var dbTableName = Fn.ImportValue("dbTableName");
/*
Some code
.
*/
}
With the disclaimer that I am not sure what language your code is in, I'm going to write in CDK's native language (which I recommend you to do as well) - Typescript.
The problem comes most likely from the fact that you are using the export within the same CDK/CFN stack. The export won't be available during stack creation, as that is part of the stack creation itself.
When you're working within a single stack, the simplest, most intuitive way of "moving data" from one construct to another is to just expose values through a public member of your class, e.g.:
class DynamoDBConstruct extends Construct {
public readonly tableName: string;
constructor(scope: Construct, id: string, props: Whatever) {
super(scope, id);
const table = new Table(this, 'Table', {
partitionKey: { name: 'id', type: AttributeType.STRING },
billingMode: BillingMode.PAY_PER_REQUEST,
// omitting table name on purpose - it will be generated by CDK
});
this.tableName = table.tableName;
}
}
Now inside your stack, you can simply use that table name:
class MyStack extends Stack {
constructor(scope: App, id: string, props: Whatever) {
const table = new DynamoDBConstruct(...);
const myOtherConstruct = new MyOtherConstruct(this, 'myOtherConstruct', {
// using table name here
tableName: table.tableName,
});
}
}
The reason for the error is that you are trying to produce and consume a Stack Output in the same stack. That won't work:
Docs: Output values are available after the stack operation is complete. Stack output values aren't available when a stack status is in any of the IN_PROGRESS status.
No worries! As #Victor says, there is a much easier alternative. Get rid of the Outputs. Instead, share data between your custom constructs by declaring public fields (e.g. public Table table) in the providing class, passing the references as props to the consuming class. This is what the CDK constructs do.
See the C# example stacks in the aws-cdk-examples repo.
I am using Wso2 IS 5.11.0, I have a requirement where I need to fetch the user attributes by calling web-service based.
Below is the sample attribute finder code:
public class CustomPIPAttributeFinder extends AbstractPIPAttributeFinder {
private static final String PIP_NAME = "CustomPIPAttributeFinder";
private static final Set<String> SUPPORTED_ATTRIBUTES;
private static final Log log = LogFactory.getLog(CustomPIPAttributeFinder.class);
static {
SUPPORTED_ATTRIBUTES = new HashSet<String>();
SUPPORTED_ATTRIBUTES.add(CustomPIPConstants.SAMPLE_ATTRIBUTE_ID);
SUPPORTED_ATTRIBUTES.add(CustomPIPConstants.SAMPLE_ATTRIBUTE_NAME);
SUPPORTED_ATTRIBUTES.add(CustomPIPConstants.SAMPLE_CATEGORY);
}
#Override
public Set<String> getAttributeValues(URI attributeType, URI attributeId, URI category, String issuer,
EvaluationCtx evaluationCtx) throws Exception {
//code
}
private String retrieveSampleName(String accessToken) {
String sampleName = null;
// TODO: Get the value of the sample name from the sampleID from the datasource
return sampleName;
}
/**
* Since we override the {#link #getAttributeValues(URI, URI, URI, String, EvaluationCtx)} this won't be called.
*/
#Override
public Set<String> getAttributeValues(String subject, String resource, String action, String environment,
String attributeId, String issuer) throws Exception {
throw new UnsupportedOperationException("Method unsupported in the context");
}
public void init(Properties properties) throws Exception {
}
public String getModuleName() {
return PIP_NAME;
}
public Set<String> getSupportedAttributes() {
return SUPPORTED_ATTRIBUTES;
}
}
In the sample code we can fetch only one attribute per request.But how can we return multiple attributes before executing policy or get multiple attributes in one request from custom attribute finder. Is there any way to achieve this flow.
As per above code request attribute find(returns only one) in the example it will increase the overhead as for each attribute lookup we are calling web-service every time as it'll increase overhead.
I'm using Spring Cloud Function 1.0.0.RELEASE and the corresponding AWS adapter to run it in AWS lambda. Is there any way to retrieve the lambda function context from the Spring application context?
I know if you implement the RequestHandler interface yourself, then you get the Context object as the second parameter of the handleRequest method (see below), but since the SpringBootRequestHandler is handling this, it's not clear to me how to access the Context object. Any ideas?
Example of implementing RequestHandler directly
public class LambdaRequestHandler implements RequestHandler<String, String> {
public String handleRequest(String input, Context context) {
context.getLogger().log("Input: " + input);
return "Hello World - " + input;
}
}
Deferring the implementation of RequestHandler to SpringBootRequestHandler
public class SomeFunctionHandler
extends SpringBootRequestHandler<SomeRequest, SomeResponse> {
}
SomeFunctionHandler extends the SpringBootRequestHandler, so it can override the handleRequest method to get access to the AWS lambda Context object.
public class SomeFunctionHandler extends SpringBootRequestHandler<SomeRequest, SomeResponse> {
private static final Logger logger = LoggerFactory.getLogger(SomeFunctionHandler.class);
#Override
public Object handleRequest(SomeRequest event, Context context) {
logger.info("ARN=" + context.getInvokedFunctionArn());
return super.handleRequest(event, context);
}
}
If you're using spring-cloud-function-adapter-aws only, #Autowired might not work.
However, you could wrap your input with org.springframework.messaging.Message.
public class FooHandler implements Function<Message<DynamodbEvent>, String> {
#Override
public String apply(Message<DynamodbEvent> message) {
// Get AWS Lambda Context
Context context = message.getHeaders().get("aws-context", Context.class);
assert context != null;
// Get the original input
DynamodbEvent payload = message.getPayload();
return payload.toString();
}
}
Source:
FunctionInvoker#handleRequest
AWSLambdaUtils#generateMessage
In case you are exposing Function as a bean, you can simply Autowire the Context object.
Example:
#Autowired
private Context context;
#Bean
public Function<String, String> uppercase() {
logger.info("ARN=" + context.getInvokedFunctionArn());
return value -> {
if (value.equals("exception")) {
throw new RuntimeException("Intentional exception which should result in HTTP 417");
}
else {
return value.toUpperCase();
}
};
}
Source : this answer.
I'm relatively new to JavaEE and web services, however, I'm using netbeans to generate my client and webservice resources. I have a resource "CustomerData" that represents a mysql database table and a value "rewardsPoints" representing a column in that table, however, I am unable to update the value due to a ConstraintViolationException, specifically:
javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'preUpdate'. Please refer to embedded ConstraintViolations for details.
I'm not familiar with the 'preUpdate' callback event, is it something I need to override? I can't seem to figure out exactly why this exception is being thrown, but, as I said, i'm very new to web service programming. Here are my classes:
#Stateless
#Path("customers")
public class CustomerDataFacadeREST extends AbstractFacade<CustomerData> {
#PersistenceContext(unitName = "CustomerPortalPU")
private EntityManager em;
public CustomerDataFacadeREST() {
super(CustomerData.class);
}
#PUT
#Path("{id}")
#Consumes({"application/xml", "application/json"})
public void edit(#PathParam("id") Integer id, CustomerData entity) {
super.edit(entity);
}
#GET
#Path("{id}")
#Produces({"application/xml", "application/json"})
public CustomerData find(#PathParam("id") Integer id) {
return super.find(id);
}
#GET
#Path("addPoints/{id}/{amount}")
#Produces({"text/plain"})
public String addPoints(#PathParam("id") Integer id, #PathParam("amount") int amount) {
CustomerData customer = find(id);
customer.getRewardsPoints(customer.getRewardsPoints() + amount);
em.persist(customer);
edit(customer);
return customer.getRewardsPoints();
}
#Override
protected EntityManager getEntityManager() {
return em;
}
}
And the CustomerData entity class:
#Entity
#Table(name = "tbl_customer_data")
#XmlRootElement
public class CustomerData implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#NotNull
#Column(name = "rewards_points")
private int rewardsPoints;
public CustomerData(Integer id, int rewardsPoints) {
this.id = id;
this.rewardsPoints = rewardsPoints;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getRewardsPoints() {
return rewardsPoints;
}
public void setRewardsPoints(int rewardsPoints) {
this.rewardsPoints = rewardsPoints;
}
}
When I try to access the URI:
http://localhost:8080/CustomerPortal/ws/customers/addPoints/1/5
to add 5 points to user with id 1 i get an HTTP 500 error and in the glassfish logs it says
[2013-11-05T03:28:11.733-0500] [glassfish 4.0] [WARNING] [ejb.system_exception] [javax.enterprise.system.container.ejb.com.sun.ejb.containers] [tid: _ThreadID=21 _ThreadName=http-listener-1(3)] [timeMillis: 1383640091733] [levelValue: 900] [[
EJB5184:A system exception occurred during an invocation on EJB CustomerDataFacadeREST, method: public java.lang.String com.webservice.entities.CustomerDataFacadeREST.addPoints(java.lang.Integer,int)]]
[2013-11-05T03:28:11.741-0500] [glassfish 4.0] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=21 _ThreadName=http-listener-1(3)] [timeMillis: 1383640091741] [levelValue: 900] [[
StandardWrapperValve[com.webservice.entities.ApplicationConfig]: Servlet.service() for servlet com.webservice.entities.ApplicationConfig threw exception
javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'preUpdate'. Please refer to embedded ConstraintViolations for details.
Any resources, insight or information regarding this issue would be extremely helpful.
The exception has little to do with web services: it has to do with Bean Validation instead.
In this case, since the Validation fails inside method addPoints (look at the stack trace) the only line that can cause it is when persisting or editing an Entity of type CustomerData. The only constraint you have in that class is that rewardsPoints should not be null. So, that's the cause of the exception.
However there some things that won't work in addPoints method:
You should check that find() method doesn't return a null object.
customer.getRewardsPoints(customer.getRewardsPoints() + amount) never sets the property (does it compile?)
EntityManager.persist() throws exception if the entity already exists. You probably want to remove that line if you only want to edit (update) the entity.
Note: I am not sure that the code you have posted is really compiling and causing that exception. That's probably caused by another version.
According to the documentation, you should be able to pass a javabean to a FreeMarker template, and it will be able to access the getters of the bean. I've been trying to do this, but have not had any luck. Here's my code where I pass the bean to the template.
public class Hello extends HttpServlet {
public static final Logger LOGGER = Logger.getLogger(Hello.class.getName());
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Configuration cfg = new Configuration();
cfg.setDirectoryForTemplateLoading(new File(this.getServletContext().getRealPath("/templates")));
cfg.setObjectWrapper(new DefaultObjectWrapper());
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
cfg.setIncompatibleImprovements(new Version(2, 3, 20)); // FreeMarker 2.3.20
final String name = req.getParameter("name");
// This works when model is a Map, but not when it is a bean
Model model = new Model();
model.setUsername(name);
Template template = cfg.getTemplate("hello.ftl");
template.process(model, resp.getWriter());
} catch (TemplateException ex) {
LOGGER.log(Level.SEVERE, "Unexpected template exception", ex);
resp.sendError(500);
}
}
private static class Model {
private String username;
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
}
When I try to access ${username} in a template, I get the following error.
The following has evaluated to null or missing:
==> username [in template "hello.ftl" at line 8, column 10]
Tip: If the failing expression is known to be legally null/missing... (snip)
The failing instruction (FTL stack trace):
----------
==> ${username} [in template "hello.ftl" at line 8, column 8]
----------
I can get the template to work correctly when I use a Map. I've tried explicitly wrapping the Model object with various TemplateModel wrappers, but nothing I try seems to work.
Any hints?
Model must be a public class for this to work.
Some other notes unrelated to the question: Use setServletContextForTemplateLoading instead setDirectoryForTemplateLoading, or else your app won't work if it's run from an unpacked .war. Also, of course you must not re-create the Configuration for each request, but I assume that's like that only for the sake of this example.