Glowroot and Axis
Unfortunately by default Glowroot does not support logging Axis’ service calls and their response time. You have to write a plugin for this, which is actually very easy once you grasp how the plugins work.
package org.glowroot.agent.plugin.axis.v.one; import javax.xml.namespace.QName; import org.glowroot.agent.plugin.api.Agent; import org.glowroot.agent.plugin.api.MessageSupplier; import org.glowroot.agent.plugin.api.ThreadContext; import org.glowroot.agent.plugin.api.ThreadContext.Priority; import org.glowroot.agent.plugin.api.Timer; import org.glowroot.agent.plugin.api.TimerName; import org.glowroot.agent.plugin.api.TraceEntry; import org.glowroot.agent.plugin.api.checker.Nullable; import org.glowroot.agent.plugin.api.weaving.BindReceiver; import org.glowroot.agent.plugin.api.weaving.BindThrowable; import org.glowroot.agent.plugin.api.weaving.BindTraveler; import org.glowroot.agent.plugin.api.weaving.OnBefore; import org.glowroot.agent.plugin.api.weaving.OnReturn; import org.glowroot.agent.plugin.api.weaving.OnThrow; import org.glowroot.agent.plugin.api.weaving.Pointcut; import org.glowroot.agent.plugin.api.weaving.Shim; public class AxisAspect { @Shim("org.apache.axis.client.Call") public interface Call { String getTargetEndpointAddress(); QName getOperationName(); } private static class TraceEntryOrTimer { private final @Nullable TraceEntry traceEntry; private final @Nullable Timer timer; private TraceEntryOrTimer(final TraceEntry traceEntry) { this.traceEntry = traceEntry; timer = null; } private TraceEntryOrTimer(final Timer timer) { this.timer = timer; traceEntry = null; } private void onReturn() { if (traceEntry != null) { traceEntry.end(); } else if (timer != null) { timer.stop(); } } private void onThrow(final Throwable t) { if (traceEntry != null) { traceEntry.endWithError(t); } else if (timer != null) { timer.stop(); } } } @Pointcut(className = "org.apache.axis.client.Call", methodName = "invoke", methodParameterTypes = {}, timerName = "axis service") public static class ResourceAdvice { private static final TimerName timerName = Agent.getTimerName(ResourceAdvice.class); @OnBefore public static @Nullable TraceEntryOrTimer onBefore(final ThreadContext context, @BindReceiver final Call call) { String transactionName = call.getTargetEndpointAddress() + "#" + call.getOperationName().getLocalPart(); context.setTransactionName(transactionName, Priority.CORE_PLUGIN); TraceEntry traceEntry = context.startServiceCallEntry("HTTP", transactionName, MessageSupplier.create("axis service: {}.{}()", call.getTargetEndpointAddress(), call.getOperationName().getLocalPart()), timerName); return new TraceEntryOrTimer(traceEntry); } @OnReturn public static void onReturn(@BindTraveler @Nullable final TraceEntryOrTimer entryOrTimer) { entryOrTimer.onReturn(); } @OnThrow public static void onThrow(@BindThrowable final Throwable t, @BindTraveler @Nullable final TraceEntryOrTimer entryOrTimer) { entryOrTimer.onThrow(t); } } }
and the corresponding glowroot.plugin.json
{
“name” : “Axis 1.4 Plugin”,
“id” : “axis”,
“aspects” : [“org.glowroot.agent.plugin.axis.v.one.AxisAspect”]
}
WTF again
Looking at legacy code, someone actually wrote a class that extends ArrayList
public class UserInterfaceModel extends ArrayList {
just because (s)he wanted to find out if a specific element was in the list!
public class UserInterfaceModel extends ArrayList { private static final long serialVersionUID = 1L; public UserInterfaceModel() {} public void addElement(UserInterfaceElement uie) { this.add(uie); } //chek if contains UserInterfaceElement in collection public boolean isContainingElement(String name) { for (int i=0;i<this.size();i++) { if (((UserInterfaceElement)this.get(i)).getName().equalsIgnoreCase(name)) { return true; } } return false; } } public class UserInterfaceElement { private String name; private String url; ... }
Instead, the said programmer could just implement the equals
method in the UserInterfaceElement
element (to check only the name
value) and just use a List
.
Amzing, just amazing.
[wsdlc] [ERROR] A class/interface with the same name “com.mydomain.layer.inbound.ws.ValidateResponse” is already in use. Use a class customizat ion to resolve this conflict.
I got the error [wsdlc] [ERROR] A class/interface with the same name "com.mydomain.layer.inbound.ws.ValidateResponse" is already in use. Use a class customization to resolve this conflict.
a few days ago when I tried to generate the Java code from a WSDL file, using Oracle’s wsdlc
Ant task. The problem is caused by having two elements with the same name, but with a different namespace, in the WSDL file. For example the following bit of code will result in the error above:
<s:element name="ValidateResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="ValidateResult" type="tns:ValidateResponse" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="ValidateResponse"> <s:complexContent mixed="false"> <s:extension base="tns:Response"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="Status" type="tns:ResponseStatus" /> <s:element minOccurs="0" maxOccurs="1" name="SubscriptionExpiry" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Allowed" type="tns:ArrayOfGuid" /> </s:sequence> </s:extension> </s:complexContent> </s:complexType>
The solution to this is to use a JAXB Customisation Binding, as explained here. By using a JAXB Customisation Binding you can essentially bind a WSDL element to another type and therefore resolve the conflict, since in the generated Java code you end up with two different artifacts.
There are two ways do it. The first one is to use an external JAXB Customisation Binding file. Unfortunately this solution did not work for me. Not sure what the problem was. The second solution is to use inline JAXB Customisation Binding code. Fortunately this solution did work for me. By inlining the JAXB binding you end up with the following bit of code
<s:element name="ValidateResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="ValidateResult" type="tns:ValidateResponse" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="ValidateResponse"> <s:annotation> <s:appinfo> <jaxb:class name="ValidateResponseComplexType"/> </s:appinfo> </s:annotation> <s:complexContent mixed="false"> <s:extension base="tns:Response"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="Status" type="tns:ResponseStatus" /> <s:element minOccurs="0" maxOccurs="1" name="SubscriptionExpiry" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Allowed" type="tns:ArrayOfGuid" /> </s:sequence> </s:extension> </s:complexContent> </s:complexType>
When the wsdlc
runs you will end up with the different classes for the ValidateResponse element: ValidateResponse
and ValidateResponseComplexType
. Of course you will also need to define the jaxb namespace: xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
in the WSDL file.
<BEA-000000> <java.lang.NegativeArraySizeException
If you get this error with WebLogic’s admin server
####<2015-12-17 14:30:14 EET> <Critical> <EmbeddedLDAP> <www.myserver.com> <AdminSOA> <VDE Replication Thread> <<anonymous>> <> <9f23d04b03212987:b0dda39:151a2d3b538:-8000-0000000000004207> <1450355414644> <BEA-000000> <java.lang.NegativeArraySizeException at com.octetstring.vde.EntryChanges.readBytes(EntryChanges.java:274) at com.octetstring.vde.EntryChanges.<init>(EntryChanges.java:72) at com.octetstring.vde.replication.BackendChangeLog.getChange(BackendChangeLog.java:548) at com.octetstring.vde.replication.Replicator.run(Replicator.java:180) at com.octetstring.vde.replication.Replication.run(Replication.java:339) > )
chances are that there are corrupted LDAP files in the <domain home>/servers/<server name>/data/ldap/ldapfiles
folder. One possible solution is to delete the changelog.data
and changelog.index
files (take a backup first of these two files) and restart the admin server.
ORA-01003: no statement parsed
Not Java-related but since I started doing a bit of PL/SQL lately and I ran into this problem, I will post this. This error, in my case, was caused by having an Oracle keyword defined as a column name. For example the following
OPEN v_customer_recs FOR SELECT sc.EVENT_ID, sc.OPEN_DATE, ND.DESCRIPTION AS COMMENT, -- This will cause the error SC.BC_DATE FROM CUSTOMERS sc, NEW_DESCRIPTION ND
will result in a ORA-01003: no statement parsed
. The problem is that the COMMENT
keyword is used as a column name. If you change this then the error will go away.
How to issue a JSON POST request to Spring Data Rest with a JPA relation
Lets say we have a class Company
which has a relation to a class User
@Entity @Table(name = "company") public class Company implements java.io.Serializable { private static final long serialVersionUID = 1L; private Long id; private String name; private User user; public Company() { } @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } @Column(name = "name", nullable = false, length = 200) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @ManyToOne(fetch = FetchType.LAZY, optional = false, targetEntity=my.example.User.class) @JoinColumn(name = "user_id") public User getUser() { return user; } public void setUser(User user) { this.user = user; }
and their corresponding repositories:
@RepositoryRestResource(collectionResourceRel = "company", path = "company") public interface CompanyRepository extends JpaRepository<Company, Long> { } @RepositoryRestResource(collectionResourceRel = "user", path = "user") public interface UserRepository extends JpaRepository<User, Long> { }
In order to add a user to the company (let’s say we want to link this company to the user with id of 2) you will need to issue the following JSON request to the company repository:
{ "name" : "a company name", "user" : "http://localhost:8086/user/2" }
The “user” variable should be the URL that is pointing to the user repository.
Yet another WTF moment
boolean found = false; for (int pr=0; pr<promotions.length; pr++) { Promotion promotion = promotions[pr]; if (promotion!=null && promotion.getName().equals(""+availablePromotions[pr][a1].getPackageId())) { externalPromotions[pr] = externalPromotions[pr]; // WTF!!! Assigning a value to itself. found = true; // Not sure what this is break; } } found = false; // As soon as the loop exits this goes back to false again. WTF!!!
The above is an actual snippet of code from a production system (the comments are mine). And yes, I am serious.
How to call a web service from Android
By far the easiest way is to use the ksoap2-android API. You need the ksoap2 jar file (with all dependencies) which can be found here and you need to add this to your classpath. In the following sample code we call a free web service, called currency convertor, which has one operation (method) that is is called ConversionRate
. If you look at the service dscription (the WSDL file), you will see that this operation takes two parameters, FromCurrency
and ToCurrency
. Lets say that we want to find out the conversion rate from USD to EUR. We implement the following Activity
package gr.panos.caller; import java.io.IOException; import org.ksoap2.SoapEnvelope; import org.ksoap2.SoapFault; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapPrimitive; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.HttpTransportSE; import org.xmlpull.v1.XmlPullParserException; import android.os.AsyncTask; import android.os.Bundle; import android.app.Activity; import android.widget.TextView; public class ConvertorCaller extends Activity { public final static String URL = "http://www.webservicex.net/CurrencyConvertor.asmx"; public static final String NAMESPACE = "http://www.webserviceX.NET/"; public static final String SOAP_ACTION = "http://www.webserviceX.NET/ConversionRate"; private static final String METHOD = "ConversionRate"; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_convertor_caller); textView = (TextView) findViewById(R.id.textView1); AsyncTaskRunner runner = new AsyncTaskRunner(); runner.execute(); } private class AsyncTaskRunner extends AsyncTask<String, String, String>{ private String resp; @Override protected String doInBackground(String... params) { try { SoapObject request = new SoapObject(NAMESPACE, METHOD); request.addProperty("FromCurrency", "USD"); request.addProperty("ToCurrency", "EUR"); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.dotNet = true; envelope.setOutputSoapObject(request); System.out.println("************ I AM SENDING: " + envelope.bodyOut); HttpTransportSE transport = new HttpTransportSE(URL); try { transport.call(SOAP_ACTION, envelope); } catch (IOException e) { e.printStackTrace(); } catch (XmlPullParserException e) { e.printStackTrace(); } if (envelope.bodyIn != null) { if (envelope.bodyIn instanceof SoapFault) { String s = ((SoapFault) envelope.bodyIn).faultstring; System.out.println("************ ERROR: " + s); resp = s; } else if (envelope.bodyIn instanceof SoapObject) { SoapObject obj = ((SoapObject) envelope.bodyIn); System.out.println("**** RESPONSE: " +obj); SoapPrimitive root = (SoapPrimitive) obj.getProperty(0); System.out.println("**** CONVERSION RATE: " +root.toString()); resp = root.toString(); } } } catch (Exception e) { e.printStackTrace(); resp = e.getMessage(); } return resp; } /** * * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(String result) { textView.setText(resp); } /** * * @see android.os.AsyncTask#onPreExecute() */ @Override protected void onPreExecute() { } /** * * @see android.os.AsyncTask#onProgressUpdate(Progress[]) */ @Override protected void onProgressUpdate(String... text) { } } }
You also need to define a text view in your layout as well as give the activity INTERNET permission in your manifest file.
And more!
if(canBeMultiplied && renewalOfferMultiplier > 0) discount = discount * renewalOfferMultiplier; else discount = discount;
Even more WTF
And I thought that I had seen everything. But I hadn’t yet seen a wrapper to a String!!
public class OneVariableGenericVO implements java.io.Serializable { private String varName; public OneVariableGenericVO() { } public OneVariableGenericVO(String varName) { setVarName(varName); } public String getVarName() { return this.varName; } public void setVarName(String varName) { this.varName = varName; } }
SO instead of using a String someone had the brilliant idea to wrap a String into an object and use this instead. What can I say? I am speechless.