Magento's Observer event isn't working and using webservices inside a Observer class - web-services

I'm trying to create a dynamic product discount using values from a webservice.
I've searched some guides on the internet about this matter and I found that I needed to use checkout_cart_product_add_after and checkout_cart_update_items_after.
However, I followed some guides. Created my own module (which is visible in Magento back office: Configuration > Advanced > Modules) and a observer for this module. I didn't create anything more but it's not working. Since I can see the module in that menu, I believe the problem is on the observer/event call.
Here is the config.xml (which is inside app\code\local\namespace\MyModule\etc) for my module:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<namespace_MyModule>
<version>0.1.0</version>
</namespace_MyModule>
</modules>
<global>
<events>
<checkout_cart_product_add_after>
<observers>
<namespace_MyModule_Discount>
<class>MyModule/Observer</class>
<method>MyModulePriceChange</method>
</namespace_MyModule_Discount>
</observers>
</checkout_cart_product_add_after>
</events>
</global>
</config>
And this is my Observer (which is inside app\code\local\namespace\MyModule\Model) for my module:
<?php
class namespace_MyModule_Model_Observer
{
public function MyModulePriceChange(Varien_Event_Observer $obs)
{
// Get the quote item
$item = $obs->getQuoteItem();
// Ensure we have the parent item, if it has one
$item = ( $item->getParentItem() ? $item->getParentItem() : $item );
// Load the custom price
$price = $this->_getPriceByItem($item);
// Set the custom price
$item->setCustomPrice($price);
$item->setOriginalCustomPrice($price);
// Enable super mode on the product.
$item->getProduct()->setIsSuperMode(true);
}
protected function _getPriceByItem(Mage_Sales_Model_Quote_Item $item)
{
$price = 4;
//use $item to determine your custom price.
return $price;
}
}
?>
Also, is it possible do call soap client to use a webservice inside a observer?
I hope my question is clear, thank you in advance for helping.

I see some issues with your config.xml. First of all, use capitalized both company name and module name. namespace_MyModule will become your namespace.
You have to declare models under global section like this:
<models>
<mycompany_mymodule>
<class>Mycompany_Mymodule_Model</class>
</mycompany_mymodule>
</models>
This will tell magento you want to use mycompany_mymodule for models in your module, and class name of each module will start with Mycompany_Mymodule_Model.
Where Mycompany and Mymodule are respective to folders of your module: app/code/local/Mycompany/Mymodule.
The modules section of config.xml should also have this namespace (Mycompany_Mymodule), matching name of your file app/etc/modules and folder structure in app/code/local.
The observers then become the following (I added type, and changed class):
<observers>
<namespace_MyModule_Discount>
<type>singleton</type>
<class>mycompany_mymodule/Observer</class>
<method>MyModulePriceChange</method>
</namespace_MyModule_Discount>
</observers>
Then try to test your observer method by adding there some code like die("message").

You haven't declared the models tag in the config.xml file.
An observer is a model after all and magento will not know where to find it (that MyModule/Observer you reference). Below it's an example of declaring models tag:
<models>
<MyModule>
<class>Namespace_Modulename_Model</class>
</MyModule>
</models>
Yes, you can do soap api calls inside observer.

Related

Enterprise Architect (C++): Import Source Code with Custom Macro and Argument in Parantheses

I want to reverse engineer models from source code with Enterprise Architect 11. The class definitions look like:
class MYCUSTOMMACRO(className) : public baseClass
{
...
}
Unfortunately, EA skips these classes, as the macro was not recognized. If I define a language macro in EA, MYCUSTOMMACRO(className) is skipped as a whole. This, again, produces a parsing error, as the class name is missing.
So, is there a way to extract the class name out of the macro and import the classes?
Thanks,
Oliver
You can get this to work by creating your own MDG technology file based off the following:
<MDG.Technology version="1.0"><Documentation id="0" name="Customer Code Module" version="1" notes="Allows using a Customer Macro as an identifier"/>
<CodeModules><CodeModule language="C++" notes=""><CodeOptions><CodeOption name="PrependGrammarDefinitions">
<![CDATA[
<identifier> ::= "MYCUSTOMMACRO" "(" <> ")"
]]>
</CodeOption>
</CodeOptions></CodeModule></CodeModules>
</MDG.Technology>
Save the code as an XML file i.e. prependgrammer.xml.
Once created you can activate it by the Extensions | MDG Technologies... dialog then hit the "Advanced..." button, then Add.
Now you can just import the C++ as per normal and it should pick up your "MYCUSTOMMACRO" defined class.
Hope this helps!

javax.xml.bind.UnmarshalException, Unable to create an instance of class

I have the following situation:
There is a tree structure for logical expressions which is used in our application and defined by a four class hierarchie:
Node is an abstract super class
OrNode is a sub class of Node representing OR
AndNode is a sub class of Node representing AND
Leaf is a sub class of Node representing a leaf node holding some data
Now the tree structure should be transfered to a web service which will do some operation on the tree (e.g. evaluating the result by gathering some other information)
The signature of that WS-Operation could be look like the following:
public TheResult evaluateTree(Node tree);
We are using JAX-WS and generate the web services classes with wsimport. First, there are no classes generated for OrNode, AndNode and Leaf. So, we added them manually. We convert the classes used on the client side to the generated classes created by wsimport. Next we want to call the web service operation with the converted tree as parameter. But here an exception occurs saying something like:
javax.xml.ws.soap.SOAPFaultException: javax.xml.bind.UnmarshalException - with linked exception: [javax.xml.bind.UnmarshalException: Unable to create an instance of InterfaceEntities.Node - with linked exception: [java.lang.InstantiationException]]
Here are the Wrapper classes added by us and generated classes:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "OrNode")
public class OrNode
extends Node
{
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "AndNode")
public class AndNode
extends Node
{
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "leaf")
public class Leaf
extends Node
{
...
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "node", propOrder = {
"children",
"resultSet",
})
#XmlSeeAlso({
Leaf.class,
OrNode.class,
AndNode.class
})
public abstract class Node {
...
}
EDIT:
Here is the generated XML-File when using Marshaller as described in Blaise Doughan's blog (see answer below):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:treeInfo xmlns:ns2="http://webservice.api.process/">
<tree xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:OrNode">
<children xsi:type="ns2:leaf">
<!-- some fields holding values -->
</children>
<children xsi:type="ns2:leaf">
<!-- some fields holding values -->
</children>
</tree>
<!-- some fields holding values -->
</ns2:treeInfo>
It is a simple tree consisting of one orNode and two leaf nodes,
treeInfo represents the class holding the Node/tree object with some other information. It is marked as the XmlRootElement with the corresponding annotation.
Did we miss anything?
Thanks in advance!
I've found the problem. We are using several web services, for each we generate the wrapper classes via wsimport. And some webservices are using the node class. Now, as I mentioned in my question, we had to implement some wrapper classes manually as they were not auto generated by wsimport, i.e. we had to add wrapper classes for the OrNode and AndNode. You also have to add the XmlSeeAlso element to the super class so that it knows its sub classes. We added the XmlSeeAlso element for one web service but missed it for the other one. This caused the exception mentioned above.
If you are in development, a quick fix is to simply remove the abstract key word from the class definition of the "abstract" class. If the class is no longer abstract, it will no longer throw the error. This may be a hack that you want to fix before releasing your code into production, but it can enable you to keep developing other aspects of your application before you resolve the deeper problem.
Assuming in your question that your SearchNode class should really be Node then you need to make sure that the XML contains the appropriate xsi:type attribute to specify the subclass.
For the Leaf class it would be:
<node xsi:type="leaf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
</node>
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html

How can I add characters at the end of my xml response?

I have a restful web service that's returning results like this:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Some Text</string>
However, the people on the receiving end need this text to be terminated w/ a special character such as "\r". How can I add that text to the end of my serialized response?
I'm sending this response from inside of a WCF service in C# like this:
[WebGet(UriTemplate = "/MyMethod?x={myId}"), OperationContract]
string GetSomeText(Guid myId);
I can think of three solutions:
1. Http Module (least code but most confusing for maintenance)
Assuming you're hosting your WCF in ASP.Net, you can create an Http module to add a \r to the end of all responses in your application.
This could be the code of the Http module. I've used 'End' as a suffix here because it's easier to read in a browser than \r, but for \r you would change the "End" in context_PostRequestHandlerExecute to "\r".
public class SuffixModule : IHttpModule
{
private HttpApplication _context;
public void Init(HttpApplication context)
{
_context = context;
_context.PostRequestHandlerExecute += context_PostRequestHandlerExecute;
}
void context_PostRequestHandlerExecute(object sender, EventArgs e)
{
// write the suffix if there is a body to this request
string contentLengthHeaderValue = _context.Response.Headers["Content-length"];
string suffix = "End";
if (!String.IsNullOrEmpty(contentLengthHeaderValue))
{
// Increase the content-length header by the length of the suffix
_context.Response.Headers["Content-length"] =
(int.Parse(contentLengthHeaderValue) + suffix.Length)
.ToString();
// and write the suffix!
_context.Response.Write(suffix);
}
}
public void Dispose()
{
// haven't worked out if I need to do anything here
}
}
Then you need to set up your module up in your web.config. The below assumes you have IIS running in Integrated Pipeline mode. If you haven't, you need to register the modules in the <system.web><httpModules> section.
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<!-- 'type' should be the fully-qualified name of the type,
followed by a comma and the name of the assembly-->
<add name="SuffixModule" type="WcfService1.SuffixModule,WcfService1"/>
</modules>
</system.webServer>
This option has the problems that it would affect all requests in your application by default and it would probably fall over if you decided to use chunked encoding.
2. Use ASP.NET MVC (changes technology but good maintainability)
Use MVC instead of WCF. You'd have far better control over your output.
3. Custom Serializer (lots of code, but less hacky than option 1)
You could write your own custom serializer. This StackOverflow question gives you pointers on how to do this. I didn't write a prototype for this because it looked as though there were many, many methods which needed to be overridden. I daresay most of them would be pretty simple delegations to the standard serializer.

XSLT get type of an attribute from DTD

Given a valid entry in the DTD of a document:
<!ATTLIST name
id CDATA #IMPLIED
attribute ENTITY #IMPLIED >
How can I get the type of an attribute during xslt transformation, given the name of the attribute and the node?
For example name/#id = 'CDATA'
and name/#attribute = 'ENTITY'
Thanks in advance!
This information is not part of the Xpath data model and isn't reported by the XML parser to XSLT (in fact you can't in general be sure the parser reads the DTD at all)
If you suspect an attribute is of type ENTITY then you can use unparsed-entity-uri(#name) XPath function added by XSLT 1 and if you get anything other than the empty string there was an unparsed entity of that name (whether or not that attribute was declared to be of ENTITY type)
I found out that using Xerxes and Xalan allows a pretty simple implementation for this problem.
First of all extend the stylesheet tag as following:
<xsl:stylesheet xmlns:java="http://xml.apache.org/xalan/java" ....
On the attribute processing template:
<xsl:template match="#*" mode="fix-entity-references">
<xsl:param name="is-entity" select="java:com.ovitas.aton.xslt.Util.isEntity(current())"/>
The code of the referenced class:
import org.apache.xerces.dom.DeferredAttrImpl;
import org.apache.xml.dtm.ref.DTMNodeIterator;
public class Util {
public static boolean isEntity(Object o) {
try {
DTMNodeIterator iter = ((DTMNodeIterator) o);
DeferredAttrImpl attrImpl = (DeferredAttrImpl) iter.getRoot();
return attrImpl.getTypeName().equals("ENTITY");
} catch (ClassCastException e) {
e.printStackTrace();
return false;
}
}
}
Naturally referenced jars must be added to classpath.
System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
The code above enables the use of the xalan transformer.
I'll accept the previous upvoted answer, because this solution is obviously based on the use of xalan and xerxes, but I wanted to add this one as well, for future generations. Maybe it will be useful to someone.

how replace XmlGregorianCalendar by Date?

I have to expose an ejb service layer via jax-ws .
I have generated the web service using jax-ws and wsimport but I'm stopped by a strange things ; Date are being mapped to XmlGregorianCalendar .
Is it possible to use classic java Date instead ?
Can you show me the right way to proceed ?
Thanks .
Edit:
this the binding file i used :
thanks , I modified slightly your xml and attached it with netbeans to the client's webservice and it worked . This the binding I used :
<jaxws:bindings node="wsdl:definitions/wsdl:types/xsd:schema"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" wsdlLocation="../wsdl/localhost_8080/web_test/Testor.wsdl" >
<jaxb:globalBindings>
<jaxb:javaType name="java.util.Date"
xmlType="xsd:dateTime"
parseMethod="lol.XsdDateTimeConverter.unmarshal"
printMethod="lol.XsdDateTimeConverter.marshalDateTime"
/><jaxb:javaType
name="java.util.Date"
xmlType="xsd:date"
parseMethod="lol.XsdDateTimeConverter.unmarshal"
printMethod="lol.XsdDateTimeConverter.marshalDate"
/>
</jaxb:globalBindings>
</jaxws:bindings>
Not tested, but should work. First create such class:
import javax.xml.bind.DatatypeConverter;
public class XsdDateTimeConverter {
public static Date unmarshal(String dateTime) {
return DatatypeConverter.parseDate(dateTime).getTime();
}
public static String marshalDate(Date date) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(date);
return DatatypeConverter.printDate(calendar);
}
public static String marshalDateTime(Date dateTime) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(dateTime);
return DatatypeConverter.printDateTime(calendar);
}
}
Then add this to custom xjb file:
<javaType
name="java.util.Date"
xmlType="xs:dateTime"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshalDateTime"
/>
<javaType
name="java.util.Date"
xmlType="xs:date"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshalDate"
/>
</globalBindings>
Not tested, but should work. Based on my answer here: JAX-WS and Joda-Time?
Thanks Tomasz. The above solution works.
But wsimport also adds its set of Adapters like Adapter1.java and Adapter2.java with its package org.w3._2001.xmlschema, which really doesnot match my own package structure.
I found a way to change this package name using another jaxb binding. Actually, I searched for this a lot and could not find this easily, so I am adding it here for anyone looking for the same.
Add the following binding in the wsimport using '-b binding.xml'. Note that wsimport can work with multiple binding files.
binding.xml content below:
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0">
<annotation><appinfo>
<jaxb:schemaBindings>
<jaxb:package name="com.abc.xyz.utils"/>
</jaxb:schemaBindings>
</appinfo></annotation>
</schema>