Archivio

Archivio dell'autore

javax.ejb.NoSuchEJBException after redeploying remote EJBs (JEE6)

Have you ever tried to remotely lookup an ejb and get NotSuchEJBException when remote ejb gets redeployed (in a EJB 3 environment)?
I did and I had to solve this problem absolutely!

Before proceeding, it’s important that you consider your own ejb usage/architectural scenario in order to understand your problem. i.e.: is your target ejb deployed to the same cluster of your client code jee components? If so, you shouldn’t get such an exception without a clear motivation (is really your target ejb up’n’running?), because application server will handle redeploying “in-same-cluster” transparently!

There is a good Ejb FAQ page for glassfish (also valid for many JEE 6 containers)  that can help you when dealing with such situations. However, the scenario I’m going to provide the solution for is actually the case you lookup an ejb outside your own cluster/instance. What happens behind the scene of an ejb remote call? It’s all about RMI-iiop. However, explaining the protocol is not the purpose of this blog post. If you need to know more about how Glassfish handles this calls , I suggest a quite old but good post about ejb Dynamic Proxies on glassfish.

So…as a starting point, not only a StackOverflow post gave me a great suggestion, but also the solution itself! In fact, Dynamic Proxies applied by Glassfish container seems not to automatically manage “retries” nor the standard specifications allow programmers to specify, in a easy and finer way, other technical aspects such as timeouts (change ORB properties for all components!?!?)   :-(

I will report here the Vetler‘s base points from StackOverflow post simply renaming some stuff and omitting the name of the ejb as @EJB attribute (this will simplify a bit  the “EjbClientInvocationHandler” usage).
Declaring CDI injection resources

public class MyCdiResources {
 @Inject
 MyEjbRemote obj;
 }

The producer makes the looked-up MyEjbRemote reference available via an injection method that delegates to our custom EjbClientInvocationHandler through a proxy.

public class MyEjbProducer {
@EJB
private MyEjbRemote obj;

@Produces
public MyEjbRemote getEjb(final InjectionPoint ip) {
InvocationHandler handler = new EjbClientInvocationHandler(this,MyEjbRemote.class,obj);
return (MyEjbRemote)Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}

So, suppose you have a glassfish-web.xml (or similarly glassfish-ejb-jar.xml) containing:

<ejb-ref>
<ejb-ref-name>mypackage.MyEjbProducer/obj</ejb-ref-name>
<jndi-name>corbaname:iiop:myremotemachine:4837#java:global/my-ear/my-remote-ejb/MyEjb!my.remote.package.MyEjbRemote</jndi-name>
</ejb-ref>

As soon as container loads the producer, it performs jndi remote call and injects MyEjbRemote stub into producer itself. When a CDI resource claims MyEjbRemote, CDI will invoke “getEjb()” method. Now, let’s go deep inside the complete custom implementation of InvocationHandler:


public class EjbClientInvocationHandler implements InvocationHandler {

  private static final Logger log = Logger.getLogger(EjbClientInvocationHandler.class.getName());
  private Object objectContainingEjbRef;
  private Object ejbRef;
  private String jndiNameRef;
  private Class<?> currentClass = null;

  private Field getCurrentClassField(){
    Field[] fz = objectContainingEjbRef.getClass().getDeclaredFields();
    for (Field field : fz) {
      if (field.getGenericType().equals(currentClass)){
        return field;
      }
    }
    throw new IllegalStateException("'"+currentClass.getName()+"' is not a field of '"+objectContainingEjbRef.getClass().getName()+"'"+
        " Class<T> for "+EjbClientInvocationHandler.class.getSimpleName()+" must be a coherent field!!");
  }

  public <T> EjbClientInvocationHandler(Object objectContainingEjbRef, Class<T> ejbRefClass, T ejbRef) {
    this.objectContainingEjbRef = objectContainingEjbRef;
    this.ejbRef = ejbRef;
    currentClass =  ejbRefClass;
    if (currentClass==null){
      throw new IllegalStateException("when instanciating "+EjbClientInvocationHandler.class.getSimpleName()+" you MUST specify the remote interface class as <T> parameter!!");
    }
    //build the ejb-ref-name that container "attaches" to ejb jndi environment. This name is actually bound to a remote reference as
    //we defined through glassfish-*.xml.
    jndiNameRef = "java:comp/env/"+objectContainingEjbRef.getClass().getName()+"/"+getCurrentClassField().getName();
    if (log.isLoggable(Level.FINEST)){
      log.finest("jndiNameRef="+jndiNameRef);
    }
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      log.fine("invoking "+ejbRef.getClass().getName()+"."+method.getName()+"...");
      return method.invoke(ejbRef, args);
    } catch (final InvocationTargetException e) {
      if (e.getTargetException() instanceof NoSuchEJBException) {
        // look up EJB again
        Context ic = new InitialContext();
        if(log.isLoggable(Level.INFO)){
          log.info("looking up again '"+jndiNameRef+"' after got NoSuchEJBException");
        }
        ejbRef = ic.lookup(jndiNameRef);
        //re-assign the new remote stub performing injection at "low level" via reflection (maybe there's a better way via CDI?)
        Field f = getCurrentClassField();
        f.setAccessible(true);
        f.set(objectContainingEjbRef, ejbRef);
        if(log.isLoggable(Level.INFO)){
          log.info("Injection successful of  '"+currentClass.getName()+"' into '"+objectContainingEjbRef.getClass().getName()+"'");
        }
        //now make call again
        return method.invoke(ejbRef, args);               
      }else{
        throw e;
      }
    }

  }
}

As you can see, the code above may be optimized but it simpy works! I’ve tested the above solution in both ejb->remoteejb call and web->remoteejb scenarios with two Glassfish 3.1.2 instances. Hope this helps. Vins

Adding HttpOnly to RESTful response (Jee6)

agosto 29, 2011 15 commenti

Consider the following classes:

As you might see RESTful version lacks in HTTPOnly attribute (introduced with RFC 6265) because it only supports the first old RFC 2109. I don’t know which is the rational behind Jersey’s choice (latest version on RFC not implemented) but I need to solve the problem anyhow!

So, as “plain-old-jee-developer” :-D , the first solution I found was to write a servlet Filter and it worked great! Using new servlet 3.0 @WebFilter is not bad but…. googling around, I realized a more neat approach: injecting my own com.sun.jersey.spi.HeaderDelegateProvider implementation.

You only have to add a text file named

META-INF/services/com.sun.jersey.spi.HeaderDelegateProvider

containing the fully qualified name of your implementation. Your implementation must be  marked as Jersey @Provider.
UPDATE: Well, actually it’s not easy and working as it should be. I realized this tecnique does not work if jersey framework libraries are loaded before and in another classloader! So I had to change implementation with a sort of patch (tested on glassfish 3.1.2.2). I also fixed parser to be case insensitive.
Here my own implementation:

//@Provider will be substantially ignored because HeaderDelegateProvider is such an internal "special"
//The only way to look up this implementation is by using standard OSGi approach directly searching via ServiceFinder.find()
//As an example, look at NewCookie2 class
public class NewCookie2HeaderDelegateProvider implements HeaderDelegateProvider<NewCookie2> {

  private static final Logger log = Logger.getLogger(NewCookie2HeaderDelegateProvider.class.getName());

  public NewCookie2HeaderDelegateProvider(){
    log.info("NewCookie2HeaderDelegateProvider() called.");
  }

  @Override
  public NewCookie2 fromString(String header) throws IllegalArgumentException {
    if (header == null) {
      throw new IllegalArgumentException("header must not be null!");
    }
    return NewCookie2Parser.parseNewCookie2(header);
  }

  @Override
  public String toString(NewCookie2 cookie) {
    StringBuilder b = new StringBuilder();

    b.append(cookie.getName()).append('=');
    WriterUtil.appendQuotedIfWhitespace(b, cookie.getValue());

    b.append(";").append(" Version=").append(cookie.getVersion());

    if (cookie.getComment() != null) {
      b.append("; Comment=");
      WriterUtil.appendQuotedIfWhitespace(b, cookie.getComment());
    }
    if (cookie.getDomain() != null) {
      b.append("; Domain=");
      WriterUtil.appendQuotedIfWhitespace(b, cookie.getDomain());
    }
    if (cookie.getPath() != null) {
      b.append("; Path=");
      WriterUtil.appendQuotedIfWhitespace(b, cookie.getPath());
    }
    if (cookie.getMaxAge()!=-1) {
      b.append("; Max-Age=");
      b.append(cookie.getMaxAge());
    }
    if (cookie.isSecure()) {
      b.append("; Secure");
    }

    if (cookie.isHttpOnly()){
      b.append("; HTTPOnly");
    }

    return b.toString();        
  }

  @Override
  public boolean supports(Class<?> type) {
    return type == NewCookie2.class;
  }
}

where my “NewCookie2” extends Jersey “NewCookie”:

@SuppressWarnings("unchecked")
public class NewCookie2 extends NewCookie {
  private static final Logger log = Logger.getLogger(NewCookie2.class.getName());
  //Provider will be searched using ServiceFinder.find() in current thread's classloader which means, our jee module.
  private static HeaderDelegateProvider<NewCookie2> provider;
  @SuppressWarnings("rawtypes")
  static ServiceFinder<HeaderDelegateProvider> providerFinder = ServiceFinder.find(HeaderDelegateProvider.class,Thread.currentThread().getContextClassLoader(), false);
  static{
    for(@SuppressWarnings("rawtypes") HeaderDelegateProvider p : providerFinder){
      log.info("searching HeaderDelegateProviders..");
      if (p.supports(NewCookie2.class)){
        provider = p;
        log.info("HeaderDelegateProvider for NewCookie2 found: "+provider.getClass().getName());
        break;
      }
    }
    if (provider==null){
      throw new IllegalStateException("There's no HeaderDelegateProvider implementation supporting NewCookie2. Please be sure META-INF/services contains right file!");
    }
  }

  private boolean httpOnly = false;

  public NewCookie2(Cookie cookie) {
    super(cookie);
  }

  public NewCookie2(Cookie cookie, boolean httpOnly) {
    super(cookie);
    this.httpOnly = httpOnly;
  }
......

   public NewCookie2(String name, String value, String path, String domain,
      int version, String comment, int maxAge, boolean secure, boolean httpOnly) {
    super(name, value, path, domain, version, comment, maxAge, secure);
    this.httpOnly = httpOnly;
  }

  public boolean isHttpOnly() {
    return httpOnly;
  }

  public void setHttpOnly(boolean httpOnly) {
    this.httpOnly = httpOnly;
  }

  /**
   * Creates a new instance of NewCookie2 by parsing the supplied string.
   * @param value the cookie string
   * @return the newly created NewCookie2
   * @throws IllegalArgumentException if the supplied string cannot be parsed
   * or is null
   */
  public static NewCookie2 valueOf(String value) throws IllegalArgumentException {
    return provider.fromString(value);
  }

  /**
   * Convert the cookie to a string suitable for use as the value of the
   * corresponding HTTP header.
   * @return a stringified cookie
   */
  @Override
  public String toString() {
    return provider.toString(this);
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = super.hashCode();
    result = prime * result + (httpOnly ? 1231 : 1237);
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!super.equals(obj)) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    NewCookie2 other = (NewCookie2) obj;
    if (httpOnly != other.httpOnly) {
      return false;
    }
    return true;
  }
}

and a new parser takes care ok HTTPOnly attribute also:

public class NewCookie2Parser {

private static class MutableNewCookie2 {
String name = null;
String value = null;
String path = null;
String domain = null;
int version = Cookie.DEFAULT_VERSION;
String comment = null;
int maxAge = NewCookie.DEFAULT_MAX_AGE;
boolean secure = false;
boolean httpOnly = false;

public MutableNewCookie2(String name, String value) {
this.name = name;
this.value = value;
}

public NewCookie2 getImmutableNewCookie2() {
return new NewCookie2(name, value, path, domain, version, comment, maxAge, secure, httpOnly);
}
}

public class NewCookie2Parser {

  private static class MutableNewCookie2 {
    String name = null;
    String value = null;
    String path = null;
    String domain = null;
    int version = Cookie.DEFAULT_VERSION;
    String comment = null;
    int maxAge = NewCookie.DEFAULT_MAX_AGE;
    boolean secure = false;
    boolean httpOnly = false;

    public MutableNewCookie2(String name, String value) {
      this.name = name;
      this.value = value;
    }

    public NewCookie2 getImmutableNewCookie2() {
      return new NewCookie2(name, value, path, domain, version, comment, maxAge, secure, httpOnly);
    }        
  }

  public static NewCookie2 parseNewCookie2(String header) {
    String bites[] = header.split("[;,]");

    MutableNewCookie2 cookie = null;
    for (String bite: bites) {
      String crumbs[] = bite.split("=", 2);
      String name = crumbs.length>0 ? crumbs[0].trim() : "";
      String value = crumbs.length>1 ? crumbs[1].trim() : "";
      if (value.startsWith("\"") && value.endsWith("\"") && value.length()>1) {
        value = value.substring(1,value.length()-1);
      }

      if (cookie == null) {
        cookie = new MutableNewCookie2(name, value);
      } else if (name.toLowerCase().startsWith("comment")) {
        cookie.comment = value;
      } else if (name.toLowerCase().startsWith("domain")) {
        cookie.domain = value;
      } else if (name.startsWith("max-age")) {
        cookie.maxAge = Integer.parseInt(value);
      } else if (name.toLowerCase().startsWith("path")) {
        cookie.path = value;
      } else if (name.toLowerCase().startsWith("secure")) {
        cookie.secure = true;
      } else if (name.toLowerCase().startsWith("version")) {
        cookie.version = Integer.parseInt(value);
      } else if (name.toLowerCase().startsWith("domain")) {
        cookie.domain = value;
      } else if (name.toLowerCase().startsWith("httponly")) {
        cookie.httpOnly = true;
      }
    }

    return cookie.getImmutableNewCookie2();
  }
}

Obviously this is not meant to be a complete RFC 6265 implementation but it fits my needs.
As an example of a usage inside your own jersey resource, here is a snippet:

@POST
public javax.ws.rs.core.Response loginRedirect(@FormParam("user") String userName,
@FormParam("password") String password){
Response.ResponseBuilder responseBuilder = null;

//check username and password and if correct:

responseBuilder = addCookie(Response.created(uriOK).status(Status.MOVED_PERMANENTLY));

return responseBuilder.build();

}

where addCookie is:

protected static Response.ResponseBuilder addCookie(Response.ResponseBuilder responseToAddTo){
Cookie authCookie = new Cookie("AUTH", "134142134", "/", ".mydomain.it");
NewCookie2 newCookie = new NewCookie2(authCookie, true);
responseToAddTo.cookie(newCookie);
CacheControl cc=new CacheControl();
cc.setMaxAge(7200);
responseToAddTo.cacheControl(cc);
return responseToAddTo;
}

Hope this helps.
Let me know!

Categorie:Java Tag:, , , ,

Glassfish-Embedded and WebFragments

luglio 12, 2011 5 commenti

I lost a couple of working days struggling for deploying JEE6 webfragment to glassfish-embedded. I had no luck looking around forums and docs about this (quite) new way of JEE6 packaging in glassfish embedded so… I found how to work-around it by myself: packaging ScatteredArchive and merging it with my web fragment jar.

Of course this is a temporary solution (till someone will solve the problem) and it’s only for unit test porpose but this allows me to go on working.. :-)

Anyway, the solution. Suppose you have a war project called test-web and a jar project (a web fragment project) called test-web-fragment, the following code assembles the war with fragment merge and deployes it.

ScatteredArchive archiveWar = new ScatteredArchive(“test-web”,ScatteredArchive.Type.WAR, new File(“../test-web/WebContent”));
archiveWar.addClassPath( new File(“../test-web/build/classes”));
archiveWar.addClassPath( new File(“../test-web-fragment/build/classes”));
File mergeXml = JEEArchiveXml.getMergedWebXml(new File(“../test-web/WebContent/WEB-INF/web.xml”), new File(“../test-web-fragment/src/META-INF/web-fragment.xml”));
archiveWar.addMetadata(mergeXml);
deployer.deploy(archiveWar.toURI());

Here is the getMergedWebXml() utility method (a bit long but with no extra JDK dependencies ;-) :

public static File getMergedWebXml(File webXmlFile,File webFragmentXmlFile  ) throws Exception{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document webXml = factory.newDocumentBuilder().parse(webXmlFile);
Document webFragmentXml = factory.newDocumentBuilder().parse(webFragmentXmlFile);
NodeList servletsWebXml = webXml.getElementsByTagName(“servlet”);
NodeList servletsWebFragmentXml = webFragmentXml.getElementsByTagName(“servlet”);
NodeList servletMappsWebXml = webXml.getElementsByTagName(“servlet-mapping”);
NodeList servletMappsWebFragmentXml = webFragmentXml.getElementsByTagName(“servlet-mapping”);

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
if (servletsWebXml.getLength()==0){
fail(webXmlFile.toString()+” does not contain servlets??”);
}
if (servletsWebFragmentXml.getLength()==0){
log.warning(webFragmentXmlFile.toString()+” does not contain servlets??”);
}else{
Node lastServlet = servletsWebXml.item(servletsWebXml.getLength()-1);
for (int i = 0; i < servletsWebFragmentXml.getLength(); i++) {
Node imported = webXml.importNode(servletsWebFragmentXml.item(i), true);
lastServlet.getParentNode().insertBefore(imported, lastServlet.getNextSibling());
imported = lastServlet;
}
}
if (servletMappsWebFragmentXml.getLength()==0){
log.warning(webFragmentXmlFile.toString()+” does not contain servletMapping??”);
}else{
if (servletMappsWebXml.getLength()==0){
log.warning(webXmlFile.toString()+” does not contain servletMapping??”);
}
Node lastServletMapping = servletMappsWebXml.item(servletMappsWebXml.getLength()-1);

for (int i = 0; i < servletMappsWebFragmentXml.getLength(); i++) {
Node imported = webXml.importNode(servletMappsWebFragmentXml.item(i), true);
lastServletMapping.getParentNode().insertBefore(imported, lastServletMapping.getNextSibling());
imported = lastServletMapping;
}
}
webXml.normalizeDocument();
DOMSource source = new DOMSource(webXml);
transformer.setOutputProperty(“{http://xml.apache.org/xslt}indent-amount”, “2”);
transformer.setOutputProperty(OutputKeys.INDENT, “yes”);
File tempDir = new File(System.getProperty(“java.io.tmpdir”)+”/mergedWebXml”+System.currentTimeMillis()+”/”);
tempDir.mkdirs();
tempDir.deleteOnExit();
File tempFileXml = new File(tempDir,”/web.xml”);
if (tempFileXml.exists()){
tempFileXml.delete();
}
tempFileXml.deleteOnExit();
StreamResult result = new StreamResult(tempFileXml);
transformer.transform(source, result);
return tempFileXml;
}

Hope this helps.
Bye!

Testing Glassfish Embedded in Eclipse without Maven

luglio 4, 2011 4 commenti

Glassfish Embedded is a great thing, specially for test-driven developers, but there are a few things to know if you’re going to use it. In this small article I will show how to setup eclipse to use glassfish-embedded without Maven.

Note: Whethever you’re planning to use Junit with Maven or not, I suggest you to read this little blog article  for a fast overview and this one for war example usage.

First of all there are different set of api (and jars) depending on what you want to do:

  • javax.ejb.embeddable.EJBContainer
    It is a standard for all ejb container implementations/vendors and its api it’s very simple (sometimes enough).
  • org.glassfish.embeddable.*
    It’s the complete embedded implementation for glassfish for all types of JEE components.

Oracle has some useful documentation about all Apis and I use it as a reference more than use guide. I found more interesting blogs googling around for real examples.

Anyway in breaf:  if you need only ejbs then use the first. If you need other JEE components use the second.
There are many examples on the web for both cases but the aim of this article is about how to setup the environment and launch Junit Tests without maven.
The above APIs can be provided in different glassfish-embedded distribution depending on: you have a glassfish installation or not, you are using org.glassfish.embeddable.* or javax.ejb.embeddable.EJBContainer only. For simplicity I suggest to download the whole-in-one distibution: glassfish-embedded-all jar. Find out the latest version into maven repository http://download.java.net/maven/glassfish/org/glassfish/extras/glassfish-embedded-all/
I know, it’s huge (about 64MB) but I preferer having all apis available and up-to-date for development rather than realizing one day I must add or change my classpath because I d0n’t have it. I also don’t want my test project to heavily depend on local glassfish installation.

Setting up Test Project

An example of test project for glassfish embedded test

Usually unit tests comes within the project itself. This have pros and cons but I prefer having a test project apart whenever is possible/worth with.

Let’s say you have an ejb project, create a simple Java Project which depens on it (inside its Build Path configuration).

Note: ejb project is a GlassFish ejb project while test project is not.

Add the dowloaded glassfish-embcdded-all jar to the test project classpath.
Before start writing your Junit class, you must define your own glassfish embedded configuration. Oracle documentation says you can use an already existing domain.xml and I think this is the best approach: you can define configurations such as datasources in xml file rather that creating such resources programmatically!

Building up the tree structure glassfish expects to find simplifies the code so I suggest you to setup a new simple glassfish installation and copy the generated structure into your test project. Notice that you don’t have to copy jar files but just config files!
In our example, the config tree structure starts with “config/glassfish” directory.

Now you are ready to write your own test class. Initialization of embedded server can be achived like:

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(“org.glassfish.ejb.embedded.glassfish.installation.root”,”../myejb-project-test/config/glassfish”);
properties.put(EJBContainer.MODULES, new File(“../myejb-project/build/classes”));
properties.put(EJBContainer.APP_NAME, “myejb-project”);
EJBContainer.createEJBContainer(properties);

Ones you have wrote your own test, if you try to lauch it with RunAs >Junit, you may encounter exceptions like:

java.net.BindException: No free port within range: 7676=com.sun.enterprise.v3.service.impl.ServiceIntitializerHandler

This is because test project inherits the GlassFish Container classpath of the ejb project. What you can do is going to run configuration of your junit test and exclude not exported classpath entries of the ejb project as shown here.

Pay attention that sometimes this setting seems not be applied the first time (I faced missing new settings just applied within Junit in Helios) but re-editing the rus-as configuration solves the problems.

Re running test now should work as expected.

Categorie:Java Tag:, ,

HttpClient 3.1 custom cookie policy

luglio 4, 2011 1 commento

In our project, during the test of my quite simple ProxyServlet implementation based on Apache HttpClient 3.1, I came across cookie rejection issue:

WARNING: Cookie rejected: “$Version=0; CICCIO=qwqedq; $Path=/; $Domain=.mydomain.it”. Illegal domain attribute “.mydomain.it”. Domain of origin: “localhost”
04-Jul-2011 10:20:18 org.apache.commons.httpclient.HttpMethodBase processCookieHeaders

Consider the test scenario:
I want to proxy a web resource such as http://dest.mydomain.it/destinationPath and I want to force my client to pass through my http://localhost:8080/MyProxyServlet  to get it.
As you can see, this is not a typical simple http proxy because MyProxyServlet actually delegates to some business rules and I also don’t want clients to know the real destination domain URL. That’s why they must call a URL like:

http://localhost:8080/MyProxyServlet/destinationPath

Running the test means:

My ProxyServlet Test (with HttpClient) –> ProxyServlet (uses HttpClient internally) –> destination URL to be proxied

The aim of ProxyServlet is to pass back to the client all the response of destinationPath call along with returning headers (and cookies).

The problem is that my test client cannot accept .mydomain.it cookie domain because of apache HttpClient cookie validation (remember: my test calls “localhost” domain!). None of the provided implementation of CookiePolicy would work because what I want to do is a complete infringement of Http RFCs!! :-O
The only way for the client to see the returning cookie from another domain is to override the specs used by Apache HttpClient 3.1:

  1. Define your own spec class:
    public class PermitAllCookiesSpec extends CookieSpecBase{public void validate(String host, int port, String path, boolean secure, final Cookie cookie) throws MalformedCookieException {….. //you can even leave this empty!

    }

  2. register and initialize your client:
    HttpClient httpclient = new HttpClient(); CookiePolicy.registerCookieSpec(“PermitAllCookiesSpec”, PermitAllCookiesSpec.class); httpclient.getParams().setCookiePolicy(“PermitAllCookiesSpec”);

That’s it!

P.S.
If you’re wondering about why I’m using apache HttpClient 3.1..well:

  1. Using JRE URLConnection is not worth with (all communities say it. google it)
  2. It’s simpler than HttpClient 4 api
  3. It’s been tested for years ;-)
  4. It contains all features I need by now.
Categorie:Java Tag:,