0

I'm trying to create a BufferedServerSocketImpl which would return a BufferedInputStream in getInputStream instead of the non-buffered one of the wrapped object (markSupported()=false), but otherwise simply forward all other calls to the wrapped SocketImpl object.

The issue I'm having is with accept and other protected abstract methods not being visible. Here's an excerpt:

public class BufferedServerSocketImpl extends SocketImpl {
    private SocketImpl internalSocketImpl;

    BufferedServerSocketImpl(SocketImpl nested) {
        this.internalSocketImpl = nested;
    }

    @Override
    protected void accept(SocketImpl s) throws IOException {
        internalSocketImpl.accept(s);
    }
    //...
}

In the above example internalSocketImpl.accept(s); is an error because the method accept(SocketImpl) from the type SocketImpl is not visible.

I know that the object I'll wrap will be a java.net.PlainServerSocketImpl or a java.net.SocksSocketImpl, but I can't extend those as they are not visible either.

Any ideas on why this is so and how I could work around it?

I considered using dynamic proxies for Socket or SocketImpl, but since they do not have interfaces I would have to either use an external library or do bytecode manipulations, neither of which are feasible for the project. Also, I'm running in an OSGi framework, so classloader shenanigans are also not feasible.

mtsvetkov
  • 885
  • 10
  • 21
  • 1
    Why do you use both inheritance and composition? – Vincent van der Weele Oct 03 '14 at 11:36
  • 1
    You can only call that method if your class would be in the same package. `protected` means in the same package (for external access) or subclasses. – M. Deinum Oct 03 '14 at 11:36
  • I'm using both inheritance and composition because I want to return the object instead of the wrapped one. And by subclassing it I figured I'd have access to its protected members, but I guess protected abstract means I only have access to `this.access` and not `s.access`? – mtsvetkov Oct 03 '14 at 11:39
  • @mtsvetkov indeed, you cannot access `s.access`. – Vincent van der Weele Oct 03 '14 at 11:47

4 Answers4

1

You are trying to use both composition and inheritance in your class. This is, in my opinion somewhat misguided.

What you should instead be doing, is to just use inheritance.

public class BufferedServerSocketImpl extends SocketImpl {

    @Override
    protected void accept(SocketImpl s) throws IOException {
        super.accept(s);
    }
    //...
}

Alternatively if you really want to use composition you can declare your class to be in the same package. However the fact, that the class is an abstract class in my opinion is an strong indicator that it shouldn't be used for composition.

A better approach, for the composition approach will be to walk down the inheritance hierarchy until a suitable candidate class is found.

Thihara
  • 7,031
  • 2
  • 29
  • 56
  • I agree it's misguided. The usage is equally misguided - trying to tie two servers that lived on two different ports under one. Since I'm facing having to rewrite large portions of both I was hoping I could mark the stream, let one try and read the headers in the request and if it fails, just reset the stream and pass it to the other. At this point it doesn't look like it's going to happen. – mtsvetkov Oct 03 '14 at 11:56
  • @mtsvetkov It seems like this is an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Why don't you edit your question to describe your underlying issue and see what solutions people propose? – Duncan Jones Oct 03 '14 at 11:56
  • @Duncan, I didn't do that because I know the proper answer to my problem is to rewrite both so that headers are processed once and the individual servers don't have to worry about it at all. That would help the code quality of the project but would be a lot more time-consuming. – mtsvetkov Oct 03 '14 at 12:01
1

If you cannot find a single interface or abstract class for your delegate object that contains all methods you want to delegate, there will be no direct way neither for extending, nor for delegating (even if you should not make both)(*).

I can only imagine 2 options : duck typing through reflection or multiple interfaces usage.

In duck typing, you never wonder what class the delegate is nor what interface it implements, you just requires it has a method with a given name and parameter types and you get it with reflection

Method method = internalSocketImpl.getClass.getMethod("accept", SocketImpl.class);
method.invoke(internalSocketImpl, s);

It is simple to implement, but has performances implication and you loose all compile time control on accessible methods. You will have to catch the exception from both getMethod (NoSuchMethodException) and invoke ( IllegalAccessException, IllegalArgumentException, InvocationTargetException)

<TL/DR mode>

In multiple interface/superclasses usage, you pass an object implementing one, and you state in the documentation that it must also implement/extend others. Compiler cannot help you in controling that, but nevertheless it should work provided the interface/superclass is implemented/extended :

ServerSocket server = (ServerSocket) internalSocketImpl;
server.accept(s);

It is much cleaner, but you must find a set of interfaces or superclasses, containing all the methods you need and implemented/extended by any of you possible delegates. You will have to test for ClassCastException.

The last option is usable if in previous use case, all methods can be found in interfaces, it is proxying. Instead of creating a new class, you only take a proxy to your delegate. But I won't elaborate on it any further, because the method accept of ServerSocket does not exist in any method implemented by ServerSocket.

</ TL/DR mode>

That was for the general case, but in your real case, I think you should use different subclasses, one for each SocketImpl subclass you want to extend, and instead use a buffering object that you put in all your subclasses as a helper for overriding the methods that you need to. That would avoid the extend + delegate pattern (*)

(*) You are extending SocketImpl, that means that your class has an instance of all fields of SocketImpl, and do not use them because you are delegating all actual processing to another object internalSocketImpl. That is what others meant by saying you mix inheritance and composition.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

you can't call internalSocketImpl.accept(s);, you should use super.accept(s);

phedro
  • 121
  • 1
  • 10
  • `super.accept(s)` cannot be called because it's an abstract method. Even if it could, it wouldn't satisfy my requirement of forwarding to the wrapped object, since preserving whether its a Plain or Socks socket is important. – mtsvetkov Oct 03 '14 at 11:44
0

This is not very safe but you may try.

On eclipse, if you're configured to see the source code, double click on SocketImpl to get its source code.

Then create a package java.net in your project and create a class named SocketImpl and package java.net using this source code.

Find the accept() method, to change its visibility to public.

Change it from your BufferedServerSocketImpl as well.

Implement all other methods required by your (new) abstract superclass.

Remember: you're cheating the classloader. Be careful.

Community
  • 1
  • 1
Leo
  • 6,480
  • 4
  • 37
  • 52
  • I'm running in an OSGi framework, so getting that to work would be quite the pain. I probably should've included that in the question. – mtsvetkov Oct 03 '14 at 12:18
  • well, eclipse also runs on OSGi. Why don't you give it a try? ;-) – Leo Oct 03 '14 at 12:19
  • Replacing a class in such a way would require me to somehow ensure that instances of it will not cross over to other bundles, since every bundle has its own classloader. Otherwise I'll probably get a ClassCastException "java.net.SocketImpl" is not an instance of "java.net.SocketImpl". – mtsvetkov Oct 03 '14 at 12:23
  • I thought the classloader would load first the JVM SocketImpl and then it would overwrite the existent SocketImpl with this custom one, so you would not have this kind of problem. But if you say so... ;-) – Leo Oct 03 '14 at 12:26