I want to write a javaagent using byte-buddy. I wan to intercept constructor of any class that is a sub class of java.sql.Connection.
my setup code:
log.info("{} profile activated", profile);
if (profile.equals("hikari")) {
startMatcher = nameStartsWith("com.zaxxer.hikari");
JDBCAPIInterceptor.profile = new HikariProfile();
} else if (profile.equals("dbcp1")) {
startMatcher = nameStartsWith("org.apache.commons.dbcp");
JDBCAPIInterceptor.profile = new DBCP1Profile();
} else if (profile.equals("dbcp2")) {
startMatcher = nameStartsWith("org.apache.commons.dbcp2");
JDBCAPIInterceptor.profile = new DBCP2Profile();
} else if (profile.equals("druid")) {
startMatcher = nameStartsWith("com.alibaba.druid.pool");
} else {
startMatcher = any();
}
new AgentBuilder.Default().type(startMatcher.and(isSubTypeOf(java.sql.Connection.class).or(isSubTypeOf(java.sql.Statement.class))))
.transform(constructorTransformer).transform(methodsTransformer).with(listener).installOn(inst);
but why constructor of org.apache.commons.dbcp2.PoolingDataSource$PoolGuardConnectionWrapper can't be intercepted while it's definitely a sub class of java.sql.Connection as it extends org.apache.commons.dbcp2.DelegatingConnection which implements java.sql.Connection?
also, my intercepting code is executed while constructor of org.apache.commons.dbcp2.DelegatingConnection being called.
public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
/**
* PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
*
* #since 2.0
*/
private class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> {
PoolGuardConnectionWrapper(final D delegate) {
super(delegate);
}
#Override
public void close() throws SQLException {
if (getDelegateInternal() != null) {
super.close();
super.setDelegate(null);
}
}
/**
* #see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
*/
#Override
public D getDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null;
}
/**
* #see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
#Override
public Connection getInnermostDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null;
}
#Override
public boolean isClosed() throws SQLException {
return getDelegateInternal() == null || super.isClosed();
}
}
}
Related
I am trying to deserialize the following json structure to object.
"policyDetail": {
"policies": {
"API_KEY": {
"isEnabled": "Yes",
"policyEnabled": true
},
"BASIC_AUTH": {
"username": "username",
"password": "password",
"policyEnabled": true
}
}
}
In this structure, API_KEY and BASIC_AUTH are java Enum type. I am trying to deserialize like this.
In Service class
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addDeserializer(Policy.class, new PolicyDeserializer());
mapper.registerModule(module);
PolicyDeserializer.java
public class PolicyDeserializer extends StdDeserializer{
public PolicyDeserializer() {
super(Policy.class);
}
protected PolicyDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Policy deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = jp.readValueAsTree();
// Here I am only getting values under API_KEY not the entire API_KEY and underneath structure.
JsonNode customField = node.findValue("API_KEY");
Policy result = null;
if(customField != null && !customField.isNull()) {
ApiKeyPolicy apiKeyPolicy = new ApiKeyPolicy();
apiKeyPolicy = mapper.readValue(customField.toString(), ApiKeyPolicy.class);
result = apiKeyPolicy;
return result;
}
return result;
}
}
Policy.java
package model.policy;
import enums.PolicyType;
public abstract class Policy {
private PolicyType policyType;
private boolean isPolicyEnabled;
public Policy(PolicyType policyType) {
this.policyType = policyType;
}
/**
* #return the isPolicyEnabled
*/
public boolean isPolicyEnabled() {
return isPolicyEnabled;
}
/**
* #param isPolicyEnabled
* the isPolicyEnabled to set
*/
public void setPolicyEnabled(boolean isPolicyEnabled) {
this.isPolicyEnabled = isPolicyEnabled;
}
}
ApiKeyPolicy.java
import enums.PolicyType;
import model.Policy;
public class ApiKeyPolicy extends Policy {
private String isEnabled;
public ApiKeyPolicy() {
super(PolicyType.API_KEY);
}
/**
* #return the isEnabled
*/
public String getIsEnabled() {
return isEnabled;
}
/**
* #param isEnabled the isEnabled to set
*/
public void setIsEnabled(String isEnabled) {
this.isEnabled = isEnabled;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "ApiKeyPolicy [isEnabled=" + isEnabled + "]";
}
}
PolicyDetail.java
import java.util.EnumMap;
import java.util.Map;
import enums.PolicyType;
public class PolicyDetail {
EnumMap<PolicyType, Policy> policyMap = null;
public PolicyDetail() {
if(policyMap == null) {
policyMap = new EnumMap<PolicyType,Policy>(PolicyType.class);
}
}
public void addPolicy(PolicyType policyType, Policy policy, boolean isEnabled) {
if(null != policy) {
policy.setPolicyEnabled(isEnabled);
this.policyMap.put(policyType, policy);
}
}
public Map<PolicyType, Policy> getPolicies(){
return this.policyMap;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "PolicyDetail [policyMap=" + policyMap + "]";
}
}
As this is how 3rd party Json structure. Hence, I can not change the existing structure. Can you please suggest where am I missing?
Thanks in advance.. !
After too much of research and hit-and-trial. I found that the Parent Abstract class Policy.java should have following entries like
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type",
visible = true
)
#JsonSubTypes({
#JsonSubTypes.Type(value = APIKeyPolicy.class, name = "API_KEY"),
#Type(value = BasicAuth.class, name = "BASIC_AUTH")})
public abstract class Policy {
//with all methods
}
So, now when I would try to deserialize this to object I should be getting following
"API_KEY": {
"type": "API_KEY",
"isEnabled": "Yes",
"policyEnabled": true
},
"BASIC_AUTH": {
"type": "BASIC_AUTH",
"username": "username",
"password": "password",
"policyEnabled": true
}
But it comes with an additional property as type which is fine as of now. as I am more interested on Policy Name ENUM with corresponding attributes.
I am using retrofit 2.3.0 to consume API's in my app but a week ago I started receiving error message and existing code was not able to display error message in UI.
Previously, I was using errorBody.toString() then suddenly after few months I got error and then last week I tried with errorBody.string() but it dodn't work. Now today it's working.
I have attached screenshots of response from server and my error handling also. Here is my code to display error message.
private static void showToastForError(retrofit2.Response<Object> response, int requestType) {
if (response != null && response.errorBody() != null) {
try {
JSONObject jObjError = null;
try {
jObjError = new JSONObject(response.errorBody() != null ? response.errorBody().toString() : "");
Toast.makeText(Application.getAppContext(), jObjError.getString("message"), Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I think you should custom call adapter to handle error.
Here my custom adapter
public final class ErrorHandlingAdapter {
/**
* A callback which offers granular callbacks for various conditions.
*/
public interface MyCallback<T> {
/**
* Called for [200, 300) responses.
*/
void success(Response<T> response);
/**
* Called for 401 responses.
*/
void unauthenticated(Response<?> response);
/**
* Called for [400, 500) responses, except 401.
*/
void clientError(Response<?> response);
/**
* Called for [500, 600) response.
*/
void serverError(Response<?> response);
/**
* Called for network errors while making the call.
*/
void networkError(IOException e);
/**
* Called for unexpected errors while making the call.
*/
void unexpectedError(Throwable t);
}
public interface MyCall<T> {
void cancel();
void enqueue(MyCallback<T> callback);
MyCall<T> clone();
boolean isExcute();
}
public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
#Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit) {
if (getRawType(returnType) != MyCall.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalStateException(
"MyCall must have generic type (e.g., MyCall<ResponseBody>)");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
Executor callbackExecutor = retrofit.callbackExecutor();
return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor);
}
private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R> {
private final Type responseType;
private final Executor callbackExecutor;
ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) {
this.responseType = responseType;
this.callbackExecutor = callbackExecutor;
}
#Override
public Type responseType() {
return responseType;
}
#Override
public <R1> R adapt(Call<R1> call) {
return (R) new MyCallAdapter(call, callbackExecutor);
}
}
}
/**
* Adapts a {#link Call} to {#link MyCall}.
*/
static class MyCallAdapter<T> implements MyCall<T> {
private final Call<T> call;
private final Executor callbackExecutor;
MyCallAdapter(Call<T> call, Executor callbackExecutor) {
this.call = call;
this.callbackExecutor = callbackExecutor;
}
#Override
public void cancel() {
call.cancel();
}
#Override
public void enqueue(final MyCallback<T> callback) {
call.enqueue(new Callback<T>() {
#Override
public void onResponse(Call<T> call, Response<T> response) {
// on that executor by submitting a Runnable. This is left as an exercise for the reader.
callbackExecutor.execute(new Runnable() {
#Override
public void run() {
int code = response.code();
if (code >= 200 && code < 300) {
callback.success(response);
} else if (code == 401) {
if (Storage.getInstance().isLogin())
Storage.getInstance().logout(App.self().getApplicationContext());
} else if (code >= 400 && code < 500) {
callback.clientError(response);
} else if (code >= 500 && code < 600) {
callback.serverError(response);
} else {
callback.unexpectedError(new RuntimeException("Unexpected response " + response));
}
}
});
}
#Override
public void onFailure(Call<T> call, Throwable t) {
// on that executor by submitting a Runnable. This is left as an exercise for the reader.
callbackExecutor.execute(new Runnable() {
#Override
public void run() {
if (t instanceof IOException) {
if (call.isCanceled()) {
return;
}
callback.networkError((IOException) t);
Toast.makeText(App.self(), R.string.error_no_connect_internet, Toast.LENGTH_SHORT).show();
} else {
callback.unexpectedError(t);
}
}
});
}
});
}
#Override
public MyCall<T> clone() {
return new MyCallAdapter<>(call.clone(), callbackExecutor);
}
#Override
public boolean isExcute() {
return call.isExecuted();
}
}
}
Here my config to add custom call adapter
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(new ErrorHandlingAdapter.ErrorHandlingCallAdapterFactory()) // custom call adapter
.addConverterFactory(GsonConverterFactory.create())
.client(getHeader())
.build();
And handle request, ex:
#GET("api/getSomething")
ErrorHandlingAdapter.MyCall<BaseResponse> getSomething(#Query("param"),...)
Handle response:
ErrorHandlingAdapter.MyCall<BaseResponse> mCalls = ApiUtils.getSomething(...);
mCalls.enqueue(new ErrorHandlingAdapter.MyCallback<BaseResponse>() {
#Override
public void success(Response<BaseResponse> response) {
//handle response
}
#Override
public void unauthenticated(Response<?> response) {
//handle unauthenticated error
}
#Override
public void clientError(Response<?> response) {
//handle clientError error
}
#Override
public void serverError(Response<?> response) {
//handle serverError error
}
#Override
public void networkError(IOException e) {
//handle networkError error
}
#Override
public void unexpectedError(Throwable t) {
//handle unexpectedError error
}
}
I'm a beginner in java and i'm practicing Singleton.
I'm trying to figure out how to convert my Database class to Singleton.
Also, part of my issue is how to use the Database class without passing the properties in the constructor.
public class Database {
public static final String DB_DRIVER_KEY = "db.driver";
public static final String DB_URL_KEY = "db.url";
public static final String DB_USER_KEY = "db.user";
public static final String DB_PASSWORD_KEY = "db.password";
private static Logger LOG = Logger.getLogger(Database.class.getName());
private static final Database theInstance = new Database();
private static Connection connection;
private static Properties properties;
private Database() {
}
public static void init(Properties properties) {
if (Database.properties == null) {
LOG.debug("Loading database properties from db.properties");
Database.properties = properties;
}
}
public static Connection getConnection() throws SQLException {
if (connection != null) {
return connection;
}
try {
connect();
} catch (ClassNotFoundException e) {
throw new SQLException(e);
}
return connection;
}
private static void connect() throws ClassNotFoundException, SQLException {
String dbDriver = properties.getProperty(DB_DRIVER_KEY);
LOG.debug(dbDriver);
Class.forName(dbDriver);
System.out.println("Driver loaded");
connection = DriverManager.getConnection(properties.getProperty(DB_URL_KEY),
properties.getProperty(DB_USER_KEY), properties.getProperty(DB_PASSWORD_KEY));
LOG.debug("Database connected");
}
/**
* Close the connections to the database
*/
public void shutdown() {
if (connection != null) {
try {
connection.close();
connection = null;
} catch (SQLException e) {
LOG.error(e.getMessage());
}
}
}
/**
* Determine if the database table exists.
*
* #param tableName
* #return true is the table exists, false otherwise
* #throws SQLException
*/
public static boolean tableExists(String tableName) throws SQLException {
DatabaseMetaData databaseMetaData = getConnection().getMetaData();
ResultSet resultSet = null;
String rsTableName = null;
try {
resultSet = databaseMetaData.getTables(connection.getCatalog(), "%", "%", null);
while (resultSet.next()) {
rsTableName = resultSet.getString("TABLE_NAME");
if (rsTableName.equalsIgnoreCase(tableName)) {
return true;
}
}
} finally {
resultSet.close();
}
return false;
}
/**
* #return the theinstance
*/
public static Database getTheinstance() {
return theInstance;
}
}
Well this little bit change into your existing class to make you understand.
public class Database {
public static final String DB_DRIVER_KEY = "db.driver";
public static final String DB_URL_KEY = "db.url";
public static final String DB_USER_KEY = "db.user";
public static final String DB_PASSWORD_KEY = "db.password";
private static Logger LOG = Logger.getLogger(Database.class.getName());
private static Database theInstance;
private static Connection connection;
private static Properties properties;
private Database() {
}
public static void init(Properties properties) {
if (Database.properties == null) {
LOG.debug("Loading database properties from db.properties");
Database.properties = properties;
}
}
public static Connection getConnection() throws SQLException {
if (connection != null) {
return connection;
}
try {
connect();
} catch (ClassNotFoundException e) {
throw new SQLException(e);
}
return connection;
}
private static void connect() throws ClassNotFoundException, SQLException {
String dbDriver = properties.getProperty(DB_DRIVER_KEY);
LOG.debug(dbDriver);
Class.forName(dbDriver);
System.out.println("Driver loaded");
connection = DriverManager.getConnection(
properties.getProperty(DB_URL_KEY),
properties.getProperty(DB_USER_KEY),
properties.getProperty(DB_PASSWORD_KEY));
LOG.debug("Database connected");
}
/**
* Close the connections to the database
*/
public void shutdown() {
if (connection != null) {
try {
connection.close();
connection = null;
} catch (SQLException e) {
LOG.error(e.getMessage());
}
}
}
/**
* Determine if the database table exists.
*
* #param tableName
* #return true is the table exists, false otherwise
* #throws SQLException
*/
public static boolean tableExists(String tableName) throws SQLException {
DatabaseMetaData databaseMetaData = getConnection().getMetaData();
ResultSet resultSet = null;
String rsTableName = null;
try {
resultSet = databaseMetaData.getTables(connection.getCatalog(),
"%", "%", null);
while (resultSet.next()) {
rsTableName = resultSet.getString("TABLE_NAME");
if (rsTableName.equalsIgnoreCase(tableName)) {
return true;
}
}
} finally {
resultSet.close();
}
return false;
}
/**
* #return the theinstance
*/
public static Database getInstance() {
if(theInstance == null){
theInstance = new Database();
}
return theInstance;
}
}
Given
classA {
long fahr;
....
and
classB {
long cels;
....
how can I map the following in Orika?
A.fahr <-> (B.cels*9)/5
Do I need customised Mapper or Filter ?
I suggest to use field level converter if both are of different data types but since they are of same data type we have to use a custom converter for entire class.
This is sample converter that suitable for this use case.
import ma.glasnost.orika.BoundMapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.converter.ConverterFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
public class EntryClass {
public static void main(String[] args) {
EntryClass ec = new EntryClass();
BoundMapperFacade<A, B> facade = getMapperFactory().getMapperFacade(A.class, B.class);
A fahr = new A(455);
B cels = facade.map(fahr);
System.out.println(cels);
A revFahr = facade.mapReverse(cels);
System.out.println(revFahr);
}
private static MapperFactory getMapperFactory() {
MapperFactory factory = new DefaultMapperFactory.Builder()
.build();
ConverterFactory cfactory = factory.getConverterFactory();
cfactory.registerConverter(new FahrCelsConverter());
factory.classMap(A.class, B.class)
.field("fahr", "cels")
.byDefault()
.register();
return factory;
}
}
public class A {
long fahr;
public A(long fahr) {
this.fahr = fahr;
}
public long getFahr() {
return fahr;
}
public void setFahr(long fahr) {
this.fahr = fahr;
}
#Override
public String toString() {
return "A [fahr=" + fahr + "]";
}
}
public class B {
long cels;
public B(long cels) {
this.cels = cels;
}
public long getCels() {
return cels;
}
public void setCels(long cels) {
this.cels = cels;
}
#Override
public String toString() {
return "B [cels=" + cels + "]";
}
}
public class FahrCelsConverter extends BidirectionalConverter<A, B>
{
#Override
public B convertTo(A source, Type<B> destinationType, MappingContext mappingContext) {
if(source != null)
{
return new B((source.fahr - 32) * 5 / 9);
}
return null;
}
#Override
public A convertFrom(B source, Type<A> destinationType, MappingContext mappingContext) {
if(source != null)
{
return new A((source.cels / 5) * 9 + 32);
}
return null;
}
}
It's more suited to use a converter (by id).
In our organisation, we implemented our own protocol over UDP and TCP to let external devices connected to the Internet exchange messages with a server that we developed using Netty (indeed!).
For testing purpose, we would like to connect those devices directly to our computers through USB/serial interface (we did not choose the serial communication library yet). We would also like to deploy/port the embedded software we developed for our devices on our computer to simulate the devices and to connect directly to our server using a named pipe for example (IPC).
In the Architecture Overview documentation of Netty, you claim that we could use Netty as well for such serial communication:
"Also, you are even able to take advantage of new transports which aren't yet written (such as serial port communication transport), again by replacing just a couple lines of constructor calls. Moreover, you can write your own transport by extending the core API."
Is anyone somewhere already developed such implementation in Netty or does someone else plan to do such implementation? I am also wondering if Netty is really well-suited for that since the Channel interface and many other ones use a SocketAddress to bind/connect to a peer?
Thank you for your suggestions, advices!
I wonder if you may be able to use the new iostream package for that. All you need here is an InputStream and Outputstream. See [1]
[1] https://github.com/netty/netty/tree/master/transport/src/main/java/io/netty/channel/iostream
It is possible to implement such a solutions. I have not meet problems with binding with SocketAddress.
I’m posting my implementation of USB connection with Netty.
Serial communication is quite simillar, I'm not posting it for brevity. However I am happy to add it as well if anyone needs it.
Here is base class for connection. A ChannelHandler shall be implemented according to communication needs.
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import java.net.SocketAddress;
public abstract class ConnectorImpl {
protected ChannelHandler handler;
protected Bootstrap bootstrap;
protected ChannelFuture channelFuture;
public ChannelFuture connect() throws Exception {
if (!isConnected()) {
channelFuture = bootstrap.connect(getSocketAddress()).sync();
}
return channelFuture.channel().closeFuture();
}
public boolean isConnected() {
try {
return channelFuture.channel().isOpen();
} catch (NullPointerException ex) {
return false;
}
}
public void close() {
if (!isConnected()) {
return;
}
try {
channelFuture.channel().close().sync();
} catch (InterruptedException e) {
}
}
protected ChannelOutboundHandlerAdapter createOutgoingErrorHandler() {
return new ChannelOutboundHandlerAdapter() {
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
final ChannelFutureListener channelFutureListener = future -> {
if (!future.isSuccess()) {
future.channel().close();
}
};
promise.addListener(channelFutureListener);
ctx.write(msg, promise);
}
};
}
public abstract SocketAddress getSocketAddress();
}
An extensions of that connector for needed type of connection together with Channel implementations is needed.
USB connector:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.timeout.ReadTimeoutHandler;
import javax.usb.UsbDevice;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
public class UsbConnectorImpl extends ConnectorImpl {
private static final int READ_TIMEOUT = 60;
private final UsbDevice usbDevice;
public UsbConnectorImpl(UsbChannelHandler handler, UsbDevice usbDevice) {
this.handler = handler;
this.usbDevice = usbDevice;
this.bootstrap = new Bootstrap()
.channel(getChannelClass())
.group(getLoop())
.handler(getChannelInitializer());
}
public EventLoopGroup getLoop() {
return new NioEventLoopGroup(1);
}
Class<UsbAsyncChannel> getChannelClass() {
return UsbAsyncChannel.class;
}
ChannelInitializer<Channel> getChannelInitializer() {
return new ChannelInitializer<Channel>() {
#Override
public void initChannel(#SuppressWarnings("NullableProblems") Channel ch) {
ch.pipeline()
.addLast("Generic encoder", new RequestEncoder())
.addLast("Decoder", new ResponseDecoder())
.addLast("Read timeout handler", new ReadTimeoutHandler(READ_TIMEOUT, TimeUnit.SECONDS))
.addLast("Outgoing Error Handler", createOutgoingErrorHandler())
.addLast("Card Reader handler", handler);
}
};
}
public SocketAddress getSocketAddress() {
return new UsbDeviceAddress(usbDevice);
}
}
USB Channel:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.FileRegion;
import io.netty.channel.nio.AbstractNioByteChannel;
import org.usb4java.LibUsb;
import javax.usb.UsbConfiguration;
import javax.usb.UsbDevice;
import javax.usb.UsbEndpoint;
import javax.usb.UsbInterface;
import javax.usb.UsbPipe;
import javax.usb.event.UsbPipeDataEvent;
import javax.usb.event.UsbPipeErrorEvent;
import javax.usb.event.UsbPipeListener;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.Pipe;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class UsbChannel extends AbstractNioByteChannel {
protected static final byte INTERFACE_BULK_PIPES = (byte) 1;
private static final AtomicInteger READ_TASK_COUNTER = new AtomicInteger();
private final UsbChannelConfig config;
protected UsbPipe outPipe = null;
protected UsbPipe inPipe = null;
private UsbDevice usbDevice;
private UsbDeviceAddress deviceAddress;
private UsbInterface usbInterface;
public UsbChannel() throws IOException {
super(null, Pipe.open().source());
config = new UsbChannelConfig(this);
}
#Override
public UsbChannelConfig config() {
return config;
}
#Override
public boolean isActive() {
return usbDevice != null;
}
#Override
protected ChannelFuture shutdownInput() {
try {
doClose();
} catch (Exception e) {
pipeline().fireExceptionCaught(e);
}
return null;
}
protected abstract ReadTask createReadTask();
protected void invokeRead() {
ReadTask task = createReadTask();
task.scheduledFuture = eventLoop().schedule(task, 0, TimeUnit.MILLISECONDS);
}
#Override
protected AbstractNioUnsafe newUnsafe() {
return new UsbUnsafe();
}
#Override
protected long doWriteFileRegion(FileRegion region) throws Exception {
throw new UnsupportedOperationException();
}
#Override
protected int doReadBytes(ByteBuf buf) throws Exception {
return 0;
}
#Override
protected Pipe.SourceChannel javaChannel() {
return (Pipe.SourceChannel) super.javaChannel();
}
#Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
UsbDeviceAddress remote = (UsbDeviceAddress) remoteAddress;
usbDevice = remote.value();
UsbConfiguration configuration = usbDevice.getActiveUsbConfiguration();
usbInterface = configuration.getUsbInterface(INTERFACE_BULK_PIPES);
usbInterface = usbInterface.getActiveSetting();
usbInterface.claim();
for (int i = 0; i < usbInterface.getUsbEndpoints().size(); i++) {
UsbEndpoint endpoint = (UsbEndpoint) usbInterface.getUsbEndpoints().get(i);
UsbPipe usbPipe = endpoint.getUsbPipe();
if (endpoint.getDirection() == LibUsb.ENDPOINT_IN) {
inPipe = usbPipe;
inPipe.open();
} else if (endpoint.getDirection() == LibUsb.ENDPOINT_OUT) {
outPipe = usbPipe;
outPipe.open();
}
if (inPipe != null && outPipe != null) {
break;
}
}
outPipe.addUsbPipeListener(new UsbPipeListener() {
#Override
public void errorEventOccurred(UsbPipeErrorEvent event) {
pipeline().fireExceptionCaught(event.getUsbException());
}
#Override
public void dataEventOccurred(UsbPipeDataEvent event) {
invokeRead();
}
});
inPipe.addUsbPipeListener(new UsbPipeListener() {
#Override
public void errorEventOccurred(UsbPipeErrorEvent event) {
pipeline().fireExceptionCaught(event.getUsbException());
}
#Override
public void dataEventOccurred(UsbPipeDataEvent event) {
pipeline().fireChannelRead(Unpooled.wrappedBuffer(event.getData(), 0, event.getData().length));
}
});
deviceAddress = remote;
return true;
}
#Override
protected void doFinishConnect() throws Exception {
}
#Override
public UsbDeviceAddress localAddress() {
return (UsbDeviceAddress) super.localAddress();
}
#Override
public UsbDeviceAddress remoteAddress() {
return (UsbDeviceAddress) super.remoteAddress();
}
#Override
protected UsbDeviceAddress localAddress0() {
return deviceAddress;
}
#Override
protected UsbDeviceAddress remoteAddress0() {
return deviceAddress;
}
#Override
protected void doBind(SocketAddress localAddress) throws Exception {
throw new UnsupportedOperationException();
}
#Override
protected void doDisconnect() throws Exception {
doClose();
}
#Override
protected void doClose() throws Exception {
try {
super.doClose();
javaChannel().close();
} finally {
if (inPipe != null) {
inPipe.close();
inPipe = null;
}
if (outPipe != null) {
outPipe.close();
outPipe = null;
}
if (usbInterface != null) {
usbInterface.release();
usbInterface = null;
}
if (usbDevice != null) {
usbDevice = null;
}
}
}
protected abstract static class ReadTask implements Runnable, ChannelFutureListener {
protected final int id;
protected ScheduledFuture<?> scheduledFuture;
public ReadTask() {
this.id = READ_TASK_COUNTER.incrementAndGet();
}
}
private final class UsbUnsafe extends AbstractNioUnsafe {
#Override
public void read() {
}
}
}