I am working on a REST web service, using JAX-RS, JPA and JAXB, for the management of games and their highscores. A game has the following properties: name, url and highscoreTableSize.
A short description of what I'm trying to do: I have the createRow() method in the controller which consumes JSON (the JSON serialization of a Game object, class Game being annotated with #XmlRootElement), which calls the static createRow() from the Game model class, and inside of it the setUrl() is called. The thing is that, for some reason, the setter is called twice.
Now what it happens is that, if the url sent in the body of the request is not valid against a pattern, after the "mysterious" first call it becomes null, and the second time the setter is called, it goes inside if (url == null), instead of going inside if (!matcher.matches()), when actually the latter is the real situation, because I've sent a mistyped URL.
Does anybody know why this is happening and how can I solve this?
Thank you in advance!
Class Game:
#Entity
#Table(name="games")
#XmlRootElement(name = "Game")
public class Game implements Serializable {
//properties
public void setUrl(String url) throws CustomWebServiceException {
String regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]";
Pattern pattern = Pattern.compile(regex);
System.out.println("URL: " + url);
if ( url == null || url.length() == 0) {
throw new CustomWebServiceException(Response.Status.BAD_REQUEST, new ErrorMessage("The url of the game is mandatory!"));
} else {
Matcher matcher = pattern.matcher(url);
if (!matcher.matches()) {
throw new CustomWebServiceException(Response.Status.BAD_REQUEST, new ErrorMessage("The url is invalid! Please check its syntax!"));
} else {
this.url = url;
}
}
}
public static Response createRow(EntityManager em, UserTransaction ut, String name, Game gameData) throws Exception {
ut.begin();
Game _game = em.find(Game.class, name);
if (_game != null) {
Util.tryRollback(ut);
ErrorMessage errorMessage = new ErrorMessage(
"The game with name " + name
+ " already exists in the database!");
throw new CustomWebServiceException(Response.Status.CONFLICT,
errorMessage);
}
String url = gameData.getUrl();
Integer highscoreTableSize = gameData.getHighscoreTableSize();
Game newGame = new Game();
newGame.setName(name);
newGame.setUrl(url);
newGame.setHighscoreTableSize(highscoreTableSize);
em.persist(newGame);
// force the persistence manager to save data to DB
ut.commit();
if (highscoreTableSize == null) {
highscoreTableSize = 7;
}
SuccessfulRequestMessage succesfulRequestMessage = new SuccessfulRequestMessage(
" Game entry created with name: " + name
+ ", url: " + url + " and highscoreTableSize: " + highscoreTableSize
+ ".");
return Response.status(Status.CREATED).entity(succesfulRequestMessage).type(MediaType.APPLICATION_JSON).build();
}
}
Controller:
#PUT
#Path("/{name}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createRow(
#PathParam("name") String name,
Game gameData) throws CustomWebServiceException {
try {
return Game.createRow(em, ut, name, gameData);
} catch (SystemException | NotSupportedException | IllegalStateException | SecurityException | HeuristicMixedException
| HeuristicRollbackException | RollbackException e) {
Util.tryRollback(ut);
ErrorMessage errorMessage = new ErrorMessage(
"Error when trying to create entry:" + e.toString()
+ " with message: " + e.getMessage());
throw new CustomWebServiceException(
Response.Status.INTERNAL_SERVER_ERROR, errorMessage);
} catch (CustomWebServiceException e) {
throw e;
} catch (Exception e) {
Util.tryRollback(ut);
ErrorMessage errorMessage = new ErrorMessage(
"During creation of game data, the following error(s) was(were) encountered: "
+ e.toString());
throw new CustomWebServiceException(Response.Status.BAD_REQUEST,
errorMessage);
}
}
Well, it should be called twice as per your code. Once during deserialization and once you do it yourself:
newGame.setUrl(url);
Using the same class for model and for representation is a bad idea in general.
IMHO,What you should do:
Separate your "JSON" game from the object you save in the database
Don't do validation in your setters. There is a Spring Validation for that. Use that to make sure that your JSON object is valid and then just go directly for the database.
You can use dozer to automatically convert model object to representation objects and vice versa
Edit:
Without using any libraries the easiest thing you can do is to move validation to a method in your controller:
void validateInput(Game game) throws Exception {
if (game == null) {
throw new Exception("Game object is not present in the request");
}
if (game.getUrl() == null || !game.maches({some-fancyreg-exp}) {
throw new Exception("Game URL is not valid");
}
//etc, check the rest of the fields
}
Call validateInput(game) in your controller. After that you can be sure that the input is valid. Quick and dirty. Let setters be setters.
Related
The actual code
package OOM.src.createOOM;
import org.eclipse.swt.internal.ole.win32.COM;
import com.sybase.stf.powerdesigner.PdCommon.*;
import com.sybase.stf.powerdesigner.PdOOM.*;
/**
* #author Xiao Wang
*
* This Java sample program shows how to use PowerDesigner Java proxies and
* OLE automation to create a PowerDesigner OOM model and display the contains of the OOM model.
*/
public class CreateOOM {
/** PowerDesigner application object */
private Application pdApp;
private int nbClasses;
/**
* Program entry point
*/
public static void main(String[] args) {
System.out.println("shhs");
// Create an instance of this class
CreateOOM createOOM1 = new CreateOOM();
// Create an OOM and display the result
createOOM1.CreateAndDisplayOOM();
}
/**
* Create an OOM and display the result
*/
public void CreateAndDisplayOOM() {
int hr = COM.OleInitialize(0);
try {
// Get PowerDesigner application instance and start PowerDesigner if necessary
pdApp = Application.getInstance();
if (pdApp != null)
{
// Create an OOM
Model newModel;
newModel = createOOM();
// Show the information of the current OOM
showOOMInfo();
// Release PowerDesigner application.
// This may close PowerDesigner if PowerDesigner was started by this program.
// pdApp.Release();
}
}
catch (Exception e) {
System.out.println("Cannot create PowerDesigner application object. Please verify that PowerDesigner is installed.");
}
finally {
if (hr == COM.S_OK)
COM.OleUninitialize();
}
}
/**
* Create an OOM for Java
*/
public Model createOOM() {
try {
// Create an OOM model, use Java as the language, create a default class diagram
// Convert the return object to PdOOM.Model proxy object
Model newModel = new Model(pdApp.CreateModel(PdOOM_Classes.cls_Model, "|Language=Java|Diagram=ClassDiagram"));
// set name and code
newModel.SetName("Customer Management");
newModel.SetCode("CustomerManagement");
//System.out.println("reach");
// Create a customer class.
// Use the fully qualified name here to avoid conflict with Java
com.sybase.stf.powerdesigner.PdOOM.Class newClass1 =
new com.sybase.stf.powerdesigner.PdOOM.Class(newModel.GetClasses().CreateNew());
newClass1.SetName("Customer");
newClass1.SetCode("Customer");
newClass1.SetComment("Customer class");
Attribute newAttribute;
// Create an id attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Id");
newAttribute.SetCode("id");
// id is the primary identifier (primary key)
newAttribute.SetPrimaryIdentifier(true);
// set the Java data type
newAttribute.SetDataType("int");
// Create a name attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Name");
newAttribute.SetCode("name");
// set the Java data type
newAttribute.SetDataType("java.lang.String");
// Create a phone attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Phone");
newAttribute.SetCode("phone");
// set the Java data type
newAttribute.SetDataType("java.lang.String");
// Create an email attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Email");
newAttribute.SetCode("email");
// set the Java data type
newAttribute.SetDataType("java.lang.String");
// Create an SalesOrder class.
// Use the fully qualified name here to avoid conflict with Java
com.sybase.stf.powerdesigner.PdOOM.Class newClass2 =
new com.sybase.stf.powerdesigner.PdOOM.Class(newModel.GetClasses().CreateNew());
newClass2.SetName("SalesOrder");
newClass2.SetCode("SalesOrder");
newClass2.SetComment("Sales order class");
// Create an orderId attribute
newAttribute = new Attribute(newClass2.GetAttributes().CreateNew());
newAttribute.SetName("Order id");
newAttribute.SetCode("orderId");
// id is the primary identifier (primary key)
newAttribute.SetPrimaryIdentifier(true);
// set the Java data type
newAttribute.SetDataType("int");
// Create an orderDate attribute
newAttribute = new Attribute(newClass2.GetAttributes().CreateNew());
newAttribute.SetName("Order date");
newAttribute.SetCode("orderDate");
// set the Java data type
newAttribute.SetDataType("java.util.Date");
// Create an association
Association association1 = new Association(newModel.GetAssociations().CreateNew());
// Set linked classes
association1.SetObject1(newClass1);
association1.SetObject2(newClass2);
// Set role A name and multiplicity
association1.SetRoleAName("customer");
association1.SetRoleAMultiplicity("1");
// Set role B name and multiplicity
association1.SetRoleBName("orders");
association1.SetRoleBMultiplicity("0..*");
// Get the default class diagram
ClassDiagram newDiagram = new ClassDiagram(newModel.GetDefaultDiagram());
// show the symbol in the default diagram if it is a class diagram
newDiagram.AttachObject(newClass1);
newDiagram.AttachObject(newClass2);
newDiagram.AttachLinkObject(association1);
return newModel;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Display information about the currnt OOM
*/
public void showOOMInfo() {
try {
// Get the current OOM model
if (!pdApp.GetActiveModel().isNull())
{
if (pdApp.GetActiveModel().IsKindOf(PdOOM_Classes.cls_Model))
{
// Convert the active model to an OOM model proxy object
Model aModel = new Model(pdApp.GetActiveModel());
// Initialize the number of classes
nbClasses = 0;
// Display a message in PowerDesigner outut window
pdApp.Output("Display the list of classes in the system output window.");
// Show classes and packages defined under the model
// Convert model proxy object to package proxy object
showPackageInfo(new com.sybase.stf.powerdesigner.PdOOM.Package(aModel));
System.out.println("There are " + nbClasses + " class(es) in this model.");
}
else
{
System.out.println("The current model is not an OOM model.");
}
}
else
{
System.out.println("There is no active model opened in PowerDesigner.");
}
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Display information about an OOM package
*/
public void showPackageInfo(com.sybase.stf.powerdesigner.PdOOM.Package aPackage) {
if (!aPackage.isNull() && !aPackage.IsShortcut())
{
// Display the number of classes and packages in the system output window
System.out.println("The " + aPackage.GetObjectType() + " '" + aPackage.GetCode() + "' contains " + aPackage.GetClasses().GetCount() + " class(es), " + aPackage.GetPackages().GetCount() + " package(s).");
// Use the fully qualified name here to avoid conflict with Java
com.sybase.stf.powerdesigner.PdOOM.Class aClass;
for (int n = 0; n < aPackage.GetClasses().GetCount(); n++)
{
nbClasses++;
if (nbClasses < 100)
{
// display class info
aClass = new com.sybase.stf.powerdesigner.PdOOM.Class(aPackage.GetClasses().Item(n));
showClassInfo(aClass);
}
else
{
if (nbClasses == 100)
System.out.println("...");
break;
}
}
// display classes of subpackages
com.sybase.stf.powerdesigner.PdOOM.Package subPackage;
for (int nPackage = 0; nPackage < aPackage.GetPackages().GetCount(); nPackage++)
{
subPackage = new com.sybase.stf.powerdesigner.PdOOM.Package(aPackage.GetPackages().Item(nPackage));
showPackageInfo(subPackage);
}
}
}
/**
* Display information about a class
*/
public void showClassInfo(com.sybase.stf.powerdesigner.PdOOM.Class aClass) {
try {
if (!aClass.isNull() && !aClass.IsShortcut())
{
System.out.println("Class No." + nbClasses + ": " + aClass.GetCode() + ", " + aClass.GetAttributes().GetCount() + " attribute(s), " + aClass.GetOperations().GetCount() + " operation(s)");
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
The issue im getting is--
org.eclipse.swt.SWTException: Action can not be performed. result = -2137456383 (com.sybase.stf.powerdesigner.com.COMException: COM Failure [HRESULT: 0x80990101])
at org.eclipse.swt.ole.win32.OLE.error(OLE.java:345)
at com.sybase.stf.powerdesigner.com.COMException.raiseOnFail(COMException.java:83)
at com.sybase.stf.powerdesigner.com.IDispatchEx.raisingInvoke(IDispatchEx.java:231)
at com.sybase.stf.powerdesigner.PdCommon.IApplication.CreateModel(IApplication.java:84)
at OOM.src.createOOM.CreateOOM.createOOM(CreateOOM.java:68)
at OOM.src.createOOM.CreateOOM.CreateAndDisplayOOM(CreateOOM.java:42)
at OOM.src.createOOM.CreateOOM.main(CreateOOM.java:28)
Caused by: com.sybase.stf.powerdesigner.com.COMException: COM Failure [HRESULT: 0x80990101]
at com.sybase.stf.powerdesigner.com.COMException.raiseOnFail(COMException.java:88)
... 5 more
There is no active model opened in PowerDesigner.
how can I resolve this?
I am trying to get data by multiple data from database on the basis of multiple Ids using Spring boot.
Basically it is a GET call which takes request parameters as a list of IDs and return response accordingly. IDs are unique in database
Url : api/details/1a,2a,3b
I am getting response as:
Get(value = "api/details/{Ids})
{
[id="1a",name="Raj", interest="Football"],
[id="2a",name="Tom", interest="Cricket"]
[id="3b",name="Kane", interest="Baseball"]
}
It is fine. But when i am giving a wrong Id, I am getting response as:
Url : api/details/xyz,abc,3b
{
null,
null,
[id="3b",name="Kane", interest="Baseball"]
}
I am expecting that instead of null it show say that the ID is not present along with Status code. Something like
{
2-Not found,3-Not Found,
id="3b",name="Kane", hobby="Baseball,
}
My controller class is like:
#RequestMapping(value = "api/details{Ids}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Game>>
getMultipleDetails(#PathVariable("Idss") String Idss) {
HttpHeaders headers = new HttpHeaders();
List<String> ids = Arrays.asList(Idss.split(","));
List<Game> list = new ArrayList<>();
Game details= null;
for (String id : ids) {
details= da.getMultipleDetails(id);
list.add(devices);
}
if (details== null) {
throw new RuntimeException(HttpStatus.NOT_FOUND.toString());
}
return new ResponseEntity<List<Game>>(list, headers, HttpStatus.OK);
}
}
My repository class is like:
public Device getMultipleDetails(String id) {
Game details= null;
try {
details= jdbcTemplate.queryForObject("SELECT * FROM table_name WHERE Id = ?",new DeviceRowMapper(), id);
} catch (Exception e) {
// Log the system generated Id
String systemRefId = String.valueOf(System.currentTimeMillis());
LOGGER.error(systemRefId, e);
//throw new DatabaseException(systemRefId, e);
}
return details;
}
Game is my model class that conatins id, name, hobby
As you're setting the ResponseEntity<List<Game>> you should only return a List with Game objects inside.
Not sure why you want to return the failed ones in the same List but as a workaround I will set id of the not found and, in the fields name and Game I will set 'Not found' instead of returning null objects. For example:
public Device getMultipleDetails(String id) {
Game details = new Game();
try {
details= jdbcTemplate.queryForObject("SELECT * FROM table_name WHERE Id = ?",new DeviceRowMapper(), id);
//If details is not null but it's empty
if (StringUtils.IsEmpty(details.getId())) {
details.setId(id);
details.setName("Not Found");
details.setGame("Not Found");
}
} catch (Exception e) {
// Log the system generated Id
String systemRefId = String.valueOf(System.currentTimeMillis());
LOGGER.error(systemRefId, e);
//If details is null it will trow null pointer exception
details = new Game();
details.setId(id);
details.setName("Not Found");
details.setGame("Not Found");
}
return details;
}
I strongly recommend you to rename the field Game in you Game class. A field should not duplicate the name of its containing class.
It's confusing to have a class member with the same name (case differences aside) as its enclosing class. This is particularly so when you consider the common practice of naming a class instance for the class itself.
Best practice dictates that any field or member with the same name as the enclosing class be renamed to be more descriptive of the particular aspect of the class it represents or holds.
I would recommend to rename it to something like typeOfGame for example.
You should manage the empty objects, and manage the message also, the code should be like this, because if not, only the last detail is the one evaluated, thats why the exception is not raised.
for (String id : ids) {
details= da.getMultipleDetails(id);
list.add(devices);
if (details== null) {
throw new RuntimeException(HttpStatus.NOT_FOUND.toString());
}
}
I have written some REST APIs using Java Servlets on Tomcat. These are my first experiences with Java and APIs and Tomcat. As I research and read about servlets, methods and parameter passing, and more recently thread safety, I realize I need some review, suggestions, and tutorial guidance from those of you who I see are far more experienced. I have found many questions / answers that seem to address pieces but my lack of experience clouds the clarity I desire.
The code below shows the top portion of one servlet example along with an example private method. I have "global" variables defined at the class level so that I may track the success of a method and determine if I need to send an error response. I do this because the method(s) already return a value.
Are those global variables creating an unsafe thread environment
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe
Though clipped for space, should I be doing all of the XML handling in the doGet method
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Should each method that accesses the same database open a Connection or should the doGet method create a Connection and pass it to each method
Assist, suggest, teach, guide to whatever you feel appropriate, or point me to the right learning resources so I may learn how to do better. Direct and constructive criticism welcome -- bashing and derogatory statements not preferred.
#WebServlet(name = "SubPlans", urlPatterns = {"*omitted*"})
public class SubPlans extends HttpServlet {
private transient ServletConfig servletConfig;
private String planSpecialNotes,
planAddlReqLinks,
legalTermsHeader,
legalTermsMemo,
httpReturnMsg;
private String[] subPlanInd = new String[4];
private boolean sc200;
private int httpReturnStatus;
private static final long serialVersionUID = 1L;
{
httpReturnStatus = 0;
httpReturnMsg = "";
sc200 = true;
planAddlReqLinks = null;
planSpecialNotes = null;
legalTermsHeader = "";
legalTermsMemo = null;
}
#Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
#Override
public ServletConfig getServletConfig() {
return servletConfig;
}
#Override
public String getServletInfo() {
return "SubPlans";
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<HashMap<String, Object>> alSubDeps = new ArrayList<HashMap<String, Object>>();
String[] coverageDates = new String[6],
depDates = new String[8];
String eeAltId = null,
eeSSN = null,
carrier = null,
logosite = null,
fmtSSN = "X",
subSQL = null,
healthPlan = null,
dentalPlan = null,
visionPlan = null,
lifePlan = null,
tier = null,
healthGroupNum = null,
effdate = null,
holdEffDate = null,
planDesc = "",
planYear = "",
summaryBenefitsLink = null;
int[][] effdates = new int[6][4];
int holdDistrictNumber = 0,
districtNumber = 0,
holdUnit = 0,
unit = 0;
boolean districtHasHSA = false;
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
try {
eeAltId = request.getParameter("*omitted*");
if ( eeAltId != null ) {
Pattern p = Pattern.compile(*omitted*);
Matcher m = p.matcher(eeAltId);
if ( m.find(0) ) {
eeSSN = getSSN(eeAltId);
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID format incorrect.";
System.err.println("Bad alternate id format " + eeAltId);
sc200 = false;
}
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID missing.";
System.err.println("alternate id not provided.");
sc200 = false;
}
if ( sc200 ) {
coverageDates = determineDates();
subSQL = buildSubSQLStatement(eeSSN, coverageDates);
alSubDeps = getSubDeps(subSQL);
if ( sc200 ) {
XMLStreamWriter writer = outputFactory.createXMLStreamWriter(response.getOutputStream());
writer.writeStartDocument("1.0");
writer.writeStartElement("subscriber");
// CLIPPED //
writer.writeEndElement(); // subscriber
writer.writeEndDocument();
if ( sc200 ) {
response.setStatus(HttpServletResponse.SC_OK);
writer.flush();
} else {
response.sendError(httpReturnStatus, httpReturnMsg);
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error writing XML");
System.err.println(e);
}
}
#Override
public void destroy() {
}
private String getPlanDescription(String planID) {
String planDesc = null;
String sqlEE = "SELECT ...";
Connection connGPD = null;
Statement stGPD = null;
ResultSet rsGPD = null;
try {
connGPD = getDbConnectionEE();
try {
stGPD = connGPD.createStatement();
planDesc = "Statement error";
try {
rsGPD = stGPD.executeQuery(sqlEE);
if ( !rsGPD.isBeforeFirst() )
planDesc = "No data";
else {
rsGPD.next();
planDesc = rsGPD.getString("Plan_Description");
}
} catch (Exception rsErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error retrieving plan description.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(rsErr);
sc200 = false;
} finally {
if ( rsGPD != null ) {
try {
rsGPD.close();
} catch (Exception rsErr) {
System.err.println("getPlanDescription: Error closing result set.");
System.err.println(rsErr);
}
}
}
} catch (Exception stErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error creating plan description statement.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(stErr);
sc200 = false;
} finally {
if ( stGPD != null ) {
try {
stGPD.close();
} catch (Exception stErr) {
System.err.println("getPlanDescription: Error closing query statement.");
System.err.println(stErr);
}
}
}
} catch (Exception connErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error closing database.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(connErr);
sc200 = false;
} finally {
if ( connGPD != null ) {
try {
connGPD.close();
} catch (Exception connErr) {
System.err.println("getPlanDescription: Error closing connection.");
System.err.println(connErr);
}
}
}
return planDesc.trim();
}
I have "global" variables defined at the class level
You have instance variables declared at the class level. There are no globals in Java.
so that I may track the success of a method and determine if I need to send an error response.
Poor technique.
I do this because the method(s) already return a value.
You should use exceptions for this if the return values are already taken.
Are those global variables creating an unsafe thread environment
Those instance variables are creating an unsafe thread environment.
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe?
Via exceptions thrown by the methods, see above. If there is no exception, send an OK response, whatever form that takes, otherwise whatever error response is appropriate to the exception.
Though clipped for space, should I be doing all of the XML handling in the doGet method
Not if it's long or repetitive (used in other places too).
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Sure, why not?
Should each method that accesses the same database open a Connection or should the doGet() method create a Connection and pass it to each method
doGet() should open the connection, pass it to each method, and infallibly close it.
NB You don't need the ServletConfig variable, or the init() or getServletConfig() methods. If you remove all this you can get it from the base class any time you need it via the getServletConfig() method you have pointlessly overridden.
The variables you have defined are instance members. They are not global and are not class-level. They are variables scoped to one instance of your servlet class.
The servlet container typically creates one instance of your servlet and sends all requests to that one instance. So you will have concurrent requests overwriting these variables’ contents unpredictably.
It can be ok for a servlet to have static variables or instance member variables, but only if their contents are thread safe and they contain no state specific to a request. For instance it would be normal to have a (log4j or java.util.logging) Logger object as a static member, where the logger is specifically designed to be called concurrently without the threads interfering with each other.
For error handling use exceptions to fail fast once something goes wrong.
Servlets are painful to write and hard to test. Consider using a MVC web framework instead. Frameworks like spring or dropwizard provide built-in capabilities that make things like data access and error handling easier, but most importantly they encourage patterns where you write separate well-focused classes that each do one thing well (and can be reasoned about and tested independently). The servlet approach tends to lead people to cram disparate functions into one increasingly-unmanageable class file, which seems to be the road you’re headed down.
Architecture: I have a web application from where I'm interacting with the Datastore and a client (raspberry pi) which is calling methods from the web application using Google Cloud Endpoints.
I have to add that I'm not very familiar with web applications and I assume that something's wrong with the setConsumed() method because I can see the call of /create in the app engine dashboard but there's no entry for /setConsumed.
I'm able to add entities to the Datastore using objectify:
//client method
private static void sendSensorData(long index, String serialNumber) throws IOException {
SensorData data = new SensorData();
data.setId(index+1);
data.setSerialNumber(serialNumber);
sensor.create(data).execute();
}
//api method in the web application
#ApiMethod(name = "create", httpMethod = "post")
public SensorData create(SensorData data, User user) {
// check if user is authenticated and authorized
if (user == null) {
log.warning("User is not authenticated");
System.out.println("Trying to authenticate user...");
createUser(user);
// throw new RuntimeException("Authentication required!");
} else if (!Constants.EMAIL_ADDRESS.equals(user.getEmail())) {
log.warning("User is not authorised, email: " + user.getEmail());
throw new RuntimeException("Not authorised!");
}
data.save();
return data;
}
//method in entity class SensorData
public Key<SensorData> save() {
return ofy().save().entity(this).now();
}
However, I'm not able to delete an entity from the datastore using the following code.
EDIT: There are many logs of the create-request in Stackdriver Logging, but none of setConsumed(). So it seems like the calls don't even reach the API although both methods are in the same class.
EDIT 2: The entity gets removed when I invoke the method from the Powershell so the problem is most likely on client side.
//client method
private static void removeSensorData(long index) throws IOException {
sensor.setConsumed(index+1);
}
//api method in the web application
#ApiMethod(name = "setConsumed", httpMethod = "put")
public void setConsumed(#Named("id") Long id, User user) {
// check if user is authenticated and authorized
if (user == null) {
log.warning("User is not authenticated");
System.out.println("Trying to authenticate user...");
createUser(user);
// throw new RuntimeException("Authentication required!");
} else if (!Constants.EMAIL_ADDRESS.equals(user.getEmail())) {
log.warning("User is not authorised, email: " + user.getEmail());
throw new RuntimeException("Not authorised!");
}
Key serialKey = KeyFactory.createKey("SensorData", id);
datastore.delete(serialKey);
}
This is what I follow to delete an entity from datastore.
public boolean deleteEntity(String propertyValue) {
String entityName = "YOUR_ENTITY_NAME";
String gql = "SELECT * FROM "+entityName +" WHERE property= "+propertyValue+"";
Query<Entity> query = Query.newGqlQueryBuilder(Query.ResultType.ENTITY, gql)
.setAllowLiteral(true).build();
try{
QueryResults<Entity> results = ds.run(query);
if (results.hasNext()) {
Entity rs = results.next();
ds.delete(rs.getKey());
return true;
}
return false;
}catch(Exception e){
logger.error(e.getMessage());
return false;
}
}
If you don't want to use literals, you can also use binding as follows:
String gql = "SELECT * FROM "+entityName+" WHERE property1= #prop1 AND property2= #prop2";
Query<Entity> query = Query.newGqlQueryBuilder(Query.ResultType.ENTITY, gql)
.setBinding("prop1", propertyValue1)
.setBinding("prop2", propertyValue2)
.build();
Hope this helps.
I was able to solve it by myself finally!
The problem was just related to the data type of the index used for removeSensorData(long index) which came out of a for-loop and therefore was an Integer instead of a long.
How can I execute try block statements in test case? My test case always goes to catch block.
I need to mock question bean, PostingResult, Question, dao all? I am confused. How I can test if/else ? Question class two fields are enums.
#Controller
#RequestMapping("/questionservice")
public class QuestionServiceController {
private static Log log = LogFactory.getLog(QuestionServiceController.class);
private final static Long SUCCESS = 000l;
private final static Long FAILURE = 999l;
#Autowired
QuestionAnswerDao questionAnswerDao;
#Autowired
QuestionAnswerDirectoryDao questionAnswerDirectoryDao;
#RequestMapping(value = "/postquestion", method = RequestMethod.POST)
public #ResponseBody PostingResult postQuestion(#RequestBody QuestionBean questionBean) {
System.out.println("In....");
PostingResult response = new PostingResult();
if (log.isDebugEnabled()) {
log.debug("QuestionBean: " + questionBean);
}
try {
System.out.println("in try");
Question question = questionAnswerDao.postQuestion(getQuestion(questionBean));
System.out.println("question"+question);
if (null != question) {
response.setStatus(SUCCESS);
response.setStatusMessage("Successfully saved..");
} else {
response.setStatusMessage("Question is null..");
response.setStatus(FAILURE);
}
} catch (Exception exp) {
if (log.isErrorEnabled()) {
log.error("Exception in processing " + questionBean + "; Exception: " + exp.toString());
}
response.setStatusMessage("Saving failed.. Exception: " + exp.toString());
response.setStatus(FAILURE);
}
return response;
}
It looks like you need to mock questionAnswerDao. Then tell Mockito to throw an exception when postQuestion() is invoked.
when(questionAnswerDao.postQuestion(/* specify args here */))
.thenThrow(new SomeException());
Then you can test the response object to see if it has the right messages and status.