Fall back mechanism for Redis Cache server (Windows) failure - asp.net-mvc

My MVC application(SQL as database server) use Redis cache(Windows) inorder to improve performance and also to reduce the load on database server. Redis cache is hosted on separate VM(Single node) and stackexchange.redis is the client used to connect to Redis server.
I want to make my application resilient to redis cache server failure.
So, Incase of Redis server failure,instead of throwing error to user,application should still be able to fetch it from database.
Is there any fall back mechanism that i can use in my application code in case of Redis Cache server failure?
Thanks in Advance.
Below is my RedisCacheService class. I'm using DI container to inject IConnectionMultiplexer.
public class RedisCacheService :IRedisCacheService
{
private readonly IConnectionMultiplexer _connectionMultiplexer;
private readonly IDatabase _redisCache;
public RedisCacheService(IConnectionMultiplexer connectionMultiplexer)
{
try
{
_connectionMultiplexer = connectionMultiplexer;
_connectionMultiplexer.ErrorMessage += _connectionMultiplexer_ErrorMessage;
_connectionMultiplexer.ConnectionFailed += _connectionMultiplexer_ConnectionFailed;
_connectionMultiplexer.ConnectionRestored += _connectionMultiplexer_ConnectionRestored;
_redisCache = _connectionMultiplexer.GetDatabase();
}
catch (Exception ex)
{
}
}
private void _connectionMultiplexer_ErrorMessage(object sender, RedisErrorEventArgs e)
{
//throw new NotImplementedException();
}
private void _connectionMultiplexer_ConnectionRestored(object sender, ConnectionFailedEventArgs e)
{
//throw new NotImplementedException();
}
private void _connectionMultiplexer_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
{
//throw new NotImplementedException();
}
public T JsonGet<T>(RedisKey key, CommandFlags flags = CommandFlags.None)
{
RedisValue cacheData = _redisCache.StringGet(key, flags);
if (!cacheData.HasValue)
return default;
T rgv = JsonConvert.DeserializeObject<T>(cacheData);
return rgv;
}
public RedisValue GetCacheData(RedisKey key, CommandFlags flags = CommandFlags.None)
{
RedisValue cacheData = _redisCache.StringGet(key, flags);
if (!cacheData.HasValue)
return default;
//T rgv = JsonConvert.DeserializeObject<T>(cacheData);
return cacheData;
}
public bool SetCacheData(RedisKey key, object value, TimeSpan? expiry = null, When when = When.Always, CommandFlags flags = CommandFlags.None)
{
if (value == null) return false;
return _redisCache.StringSet(key, JsonConvert.SerializeObject(value), expiry, when, flags);
}
}

Related

How do I connect to a UNIX domain socket running an HTTP server using Netty?

I am trying to connect to a Docker UNIX domain socket using Netty. Here's my attempt so far.
#PostConstruct
public void init() throws Exception {
io.netty.bootstrap.Bootstrap bootstrap = new io.netty.bootstrap.Bootstrap();
bootstrap
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.remoteAddress(new DomainSocketAddress("/var/run/docker.sock"))
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel
.pipeline()
.addLast(new SimpleChannelInboundHandler<HttpObject>() {
#Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
System.out.println(httpObject);
}
});
}
});
final Channel channel = bootstrap.connect().sync().channel();
final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/services", Unpooled.EMPTY_BUFFER);
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
channel.writeAndFlush(request);
channel.closeFuture().sync();
System.out.println("DONE");
}
At the moment I am getting
Caused by: java.nio.channels.UnsupportedAddressTypeException: null
Is there an example on how to do HTTP connections to UDS using Netty? So far I only found raw UDS and TCP HTTP but not combined.
Here's a working implementation.
io.netty.bootstrap.Bootstrap bootstrap = new io.netty.bootstrap.Bootstrap();
final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup();
try {
bootstrap
.group(epollEventLoopGroup)
.channel(EpollDomainSocketChannel.class)
.handler(new ChannelInitializer<UnixChannel>() {
#Override
public void initChannel(UnixChannel ch) throws Exception {
ch
.pipeline()
.addLast(new HttpClientCodec())
.addLast(new HttpContentDecompressor())
.addLast(new SimpleChannelInboundHandler<HttpObject>() {
private StringBuilder messageBuilder = new StringBuilder();
#Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
messageBuilder.append(content.content().toString(StandardCharsets.UTF_8));
if (msg instanceof LastHttpContent) {
System.out.println(messageBuilder);
}
} else {
System.out.println(msg.getClass());
}
}
});
}
});
final Channel channel = bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel();
final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/services", Unpooled.EMPTY_BUFFER);
request.headers().set(HttpHeaderNames.HOST, "daemon");
channel.writeAndFlush(request);
channel.closeFuture().sync();
} finally {
epollEventLoopGroup.shutdownGracefully();
}
Few things to note:
Use the EpollEventLoopGroup and EpollDomainSocketChannel with a ChannelInitializer<UnixChannel>.
HTTP requires new HttpCodec() in the pipeline to use the Netty HTTP objects.
The data may be chunked so you need to assemble it and wait for the LastHttpContent object
https://github.com/trajano/netty-docker-daemon-socket

About good practice for GLib.Application and Soup.Server

I'm trying to create a simple server in vala using libsoup.
I am wondering if it is a good way to start a Soup.Server from a GLib.Application. Since using it synchronously (run is deprecated) is not recommended, the only way I found to keep it alive is to hold the default application.
public class Simple.Server : Soup.Server
{
public Server () {
Application.get_default ().hold ();
add_handler(null, null_handler);
}
private void null_handler (Soup.Server server, Soup.Message message,
string path, HashTable<string,string>? query,
Soup.ClientContext client) {
GLib.message ("path: %s", path);
message.status_code = 404;
message.set_response ("text/plain", Soup.MemoryUse.COPY, "".data);
}
}
public class Simple.App : Application
{
private Simple.Server server;
App () {
Object (application_id: "org.dev.simple-server",
flags: ApplicationFlags.FLAGS_NONE);
}
protected override void activate () {
base.activate ();
server = new Simple.Server();
try {
server.listen_all(8080, 0);
}
catch (Error e) {
GLib.message ("Error n°%u: %s", e.code, e.message);
}
}
protected override void shutdown () {
base.shutdown ();
server.disconnect ();
}
static int main (string[] args) {
App app = new Simple.App();
return app.run (args);
}
}
This is mimic of my code.
So here is the question, is it a good practice for starting the server, still using GLib.Application, or should I use (like examples say) only the server, starting/stopping manually the MainLoop ?
thanks.

Jenkins Dynamic Locking

I am an automation engineer, and I use Jenkins for our automated tests.
I have to test each test on multiple platforms, so a build may have these parameters;
OS (Windows 7, Windows 8, XP 64bit, XP 32bit, etc...)
Server (Server of our product, version x, version y, etc...)
Product Version (x, y, etc...)
And more...
The OS chosen determines which VM (Virtual Machine) will be used as the testing grounds.
The thing is, I have many such tests, and those who run the tests do not always check what VM is already in use, or if they set an automatic test during another automatic test's time with a specific VM.
I want the build to wait until the VM is clear to be used.
I tried to play around with the Locks and Latches plugin - changing the plugin to check each lock if it's name appears in the build parameters, and if it does, check it's value. So if the lock's name is "OS Type", and the build has the parameter "OS Type = Windows 7" it would mean the build searches for the lock "Windows 7" to see if it is free or not.
I managed to do the above part - but now when I run the tests, the first test builds it's environment, and the other tests wait for it to finish the entire build, without even checking for locks!
Thanks to that, I don't even know if what I did works.
Can anyone help? Did anyone do something like that?
I will post the code below, but as I said, I am not sure if it works as intended.
Thanks in advance!
public class LockWrapper extends BuildWrapper implements ResourceActivity {
private List<LockWaitConfig> locks;
public LockWrapper(List<LockWaitConfig> locks) {
for(LockWaitConfig lock : locks)
{
}
this.locks = locks;
}
public List<LockWaitConfig> getLocks() {
return locks;
}
public void setLocks(List<LockWaitConfig> locks) {
this.locks = locks;
}
#Override
public Descriptor<BuildWrapper> getDescriptor() {
return DESCRIPTOR;
}
#Extension
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
/**
* #see ResourceActivity#getResourceList()
*/
public ResourceList getResourceList() {
ResourceList resources = new ResourceList();
for (LockWaitConfig lock : locks) {
resources.w(new Resource(null, "dynamic-locks/" + lock.getName(), DESCRIPTOR.getWriteLockCount()));
}
return resources;
}
#Override
public Environment setUp(AbstractBuild abstractBuild, Launcher launcher, BuildListener buildListener) throws IOException, InterruptedException {
final List<NamedReentrantLock> backups = new ArrayList<NamedReentrantLock>();
List<LockWaitConfig> locks = new ArrayList<LockWaitConfig>(this.locks);
// sort this list of locks so that we _always_ ask for the locks in order
Collections.sort(locks, new Comparator<LockWaitConfig>() {
public int compare(LockWaitConfig o1, LockWaitConfig o2) {
return o1.getName().compareTo(o2.getName());
}
});
// build the list of "real" locks
for (LockWaitConfig lock : locks) {
NamedReentrantLock backupLock;
String varName = lock.getName();
String temp = varName;
if(abstractBuild.getBuildVariables().containsKey(varName))
{
temp = abstractBuild.getBuildVariables().get(varName).toString();
buildListener.getLogger().println("Variable " + varName + " found, replacing it with the value '" + temp + "'");
}
do {
backupLock = DESCRIPTOR.backupLocks.get(temp);
if (backupLock == null) {
DESCRIPTOR.backupLocks.putIfAbsent(temp, new NamedReentrantLock(temp));
}
} while (backupLock == null);
backups.add(backupLock);
}
final StringBuilder locksToGet = new StringBuilder();
CollectionUtils.forAllDo(backups, new Closure() {
public void execute(Object input) {
locksToGet.append(((NamedReentrantLock) input).getName()).append(", ");
}
});
buildListener.getLogger().println("[Dynamic Locks] Locks to get: " + locksToGet.substring(0, locksToGet.length()-2));
boolean haveAll = false;
while (!haveAll) {
haveAll = true;
List<NamedReentrantLock> locked = new ArrayList<NamedReentrantLock>();
DESCRIPTOR.lockingLock.lock();
try {
for (NamedReentrantLock lock : backups) {
buildListener.getLogger().print("[Dynamic Locks] Trying to get " + lock.getName() + "... ");
if (lock.tryLock()) {
buildListener.getLogger().println(" Success");
locked.add(lock);
} else {
buildListener.getLogger().println(" Failed, releasing all locks");
haveAll = false;
break;
}
}
if (!haveAll) {
// release them all
for (ReentrantLock lock : locked) {
lock.unlock();
}
}
} finally {
DESCRIPTOR.lockingLock.unlock();
}
if (!haveAll) {
buildListener.getLogger().println("[Dynamic Locks] Could not get all the locks, sleeping for 1 minute...");
TimeUnit.SECONDS.sleep(60);
}
}
buildListener.getLogger().println("[Dynamic Locks] Have all the locks, build can start");
return new Environment() {
#Override
public boolean tearDown(AbstractBuild abstractBuild, BuildListener buildListener) throws IOException, InterruptedException {
buildListener.getLogger().println("[Dynamic Locks] Releasing all the locks");
for (ReentrantLock lock : backups) {
lock.unlock();
}
buildListener.getLogger().println("[Dynamic Locks] All the locks released");
return super.tearDown(abstractBuild, buildListener);
}
};
}
public void makeBuildVariables(AbstractBuild build, Map<String,String> variables) {
final StringBuilder names = new StringBuilder();
for (LockWaitConfig lock : locks) {
if (names.length() > 0) {
names.append(',');
}
names.append(lock.getName());
}
variables.put("LOCKS", names.toString());
}
public String getDisplayName() {
return DESCRIPTOR.getDisplayName();
}
public static final class DescriptorImpl extends Descriptor<BuildWrapper> {
private List<LockConfig> locks;
/**
* Required to work around HUDSON-2450.
*/
private transient ConcurrentMap<String, NamedReentrantLock> backupLocks =
new ConcurrentHashMap<String, NamedReentrantLock>();
/**
* Used to guarantee exclusivity when a build tries to get all its locks.
*/
private transient ReentrantLock lockingLock = new ReentrantLock();
DescriptorImpl() {
super(LockWrapper.class);
load();
}
public String getDisplayName() {
return "Locks";
}
#Override
public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) throws FormException {
List<LockWaitConfig> locks = req.bindParametersToList(LockWaitConfig.class, "locks.locks.");
return new LockWrapper(locks);
}
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindParameters(this, "locks.");
locks = req.bindParametersToList(LockConfig.class, "locks.lock.");
save();
return super.configure(req, formData);
}
#Override
public synchronized void save() {
// let's remove blank locks
CollectionUtils.filter(getLocks(), new Predicate() {
public boolean evaluate(Object object) {
return StringUtils.isNotBlank(((LockConfig) object).getName());
}
});
// now, we can safely sort remaining locks
Collections.sort(this.locks, new Comparator<LockConfig>() {
public int compare(LockConfig lock1, LockConfig lock2) {
return lock1.getName().compareToIgnoreCase(lock2.getName());
}
});
super.save();
}
public List<LockConfig> getLocks() {
if (locks == null) {
locks = new ArrayList<LockConfig>();
// provide default if we have none
locks.add(new LockConfig("(default)"));
}
return locks;
}
public void setLocks(List<LockConfig> locks) {
this.locks = locks;
}
public LockConfig getLock(String name) {
for (LockConfig host : locks) {
if (name.equals(host.getName())) {
return host;
}
}
return null;
}
public String[] getLockNames() {
getLocks();
String[] result = new String[locks.size()];
for (int i = 0; i < result.length; i++) {
result[i] = locks.get(i).getName();
}
return result;
}
public void addLock(LockConfig hostConfig) {
locks.add(hostConfig);
save();
}
/**
* There wass a bug in the ResourceList.isCollidingWith,
* this method used to determine the hack workaround if the bug is not fixed, but now only needs to
* return 1.
*/
synchronized int getWriteLockCount() {
return 1;
}
}
public static final class LockConfig implements Serializable {
private String name;
private transient AbstractBuild owner = null;
public LockConfig() {
}
#DataBoundConstructor
public LockConfig(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LockConfig that = (LockConfig) o;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return true;
}
#Override
public int hashCode() {
int result;
result = (name != null ? name.hashCode() : 0);
return result;
}
}
public static final class LockWaitConfig implements Serializable {
private String name;
private transient LockConfig lock;
public LockWaitConfig() {
}
#DataBoundConstructor
public LockWaitConfig(String name) {
this.name = name;
}
public LockConfig getLock() {
if (lock == null && name != null && !"".equals(name)) {
setLock(DESCRIPTOR.getLock(name));
}
return lock;
}
public void setLock(LockConfig lock) {
this.lock = lock;
}
public String getName() {
if (lock == null) {
return name;
}
return name = lock.getName();
}
public void setName(String name) {
setLock(DESCRIPTOR.getLock(this.name = name));
}
}
/**
* Extends {#code ReentrantLock} to add a {#link #name} attribute (mainly
* for display purposes).
*/
public static final class NamedReentrantLock extends ReentrantLock {
private String name;
public NamedReentrantLock(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static final Logger LOGGER = Logger.getLogger(LockWrapper.class.getName());
}
What I changed is basically this;
for (LockWaitConfig lock : locks) {
NamedReentrantLock backupLock;
String varName = lock.getName();
String temp = varName;
if(abstractBuild.getBuildVariables().containsKey(varName))
{
temp = abstractBuild.getBuildVariables().get(varName).toString();
buildListener.getLogger().println("Variable " + varName + " found, replacing it with the value '" + temp + "'");
}
do {
backupLock = DESCRIPTOR.backupLocks.get(temp);
if (backupLock == null) {
DESCRIPTOR.backupLocks.putIfAbsent(temp, new NamedReentrantLock(temp));
}
} while (backupLock == null);
backups.add(backupLock);
}
To clarify with another example (Thank you Peter Schuetze for bringing this up)
I am trying to run different jobs that may have the same resource (testing environment)
For this example I will have two different jobs;
Job A runs some tests on any VM I choose.
Job B runs some other test on any VM I choose.
If I choose Job A to run on VM 'Windows 7', and someone else tries to run Job B on VM 'Windows 7' after Job A started running, I want Job B to be blocked until Job A is finished.
I could have many Job A and Job B variants, each set to work on a different VM, but considering my platform matrix, it would be too much to handle.
If I want to avoid using the Locks plugin, the test list will look like that;
Job A - Windows 7 - Server A
Job A - Windows 7 - Server B
Job A - Windows 8 - Server A
Job A - Windows 8 - Server B
Job A - Windows XP x64 - Server A
Job A - Windows XP x64 - Server B
Job A - Windows XP x86 - Server A
Job A - Windows XP x86 - Server B
Job B - Windows 7 - Server A
Job B - Windows 7 - Server B
Job B - Windows 8 - Server A
Job B - Windows 8 - Server B
Job B - Windows XP x64 - Server A
Job B - Windows XP x64 - Server B
Job B - Windows XP x86 - Server A
Job B - Windows XP x86 - Server B
Please consider that in reality I have ... Around 20 jobs right now, each using more or less the same resources (Testing environments, servers, etc...)
Right now I have made it so my job list is like that;
Job A - $OS_TYPE - $SERVER - $Variable - $Another_Variable
Job B - $OS_TYPE - $SERVER - $Variable - $Another_Variable
And to make sure no resource is used at the same time by more than one job, I need the locks plugin, and I need it to accept a variable as a parameter.
If you have any further questions or a need for clarification, please feel free to ask :)
Just to recap. You modified the logs and latches plugin to accept dynamic labels. You now try to run the different tests with the same job.
Did you configure the job to run concurrently? This setting allows to instances of the same job to run in parallel.
BTW, this sounds like the typical use case for a multi-configuration project.

c# server and android client,connectivity

i am trying to develop an application in c# which acts as a server for an android phone.i am using 32feet.net for bluetooth in c# and i have a server running in android, which simply sends a socket to server. the server running in pc need to listen the connection and display ,the status of connection. all these things are base for my project. the server code is as shown :
namespace testserver
{
class Program
{
static void Main(string[] args)
{
BluetoothClient bc = new BluetoothClient();
BluetoothDeviceInfo[] dev;
BluetoothDeviceInfo td=null;
Guid id = new Guid("{00112233-4455-6677-8899-aabbccddeeff}");
// Console.WriteLine(id.ToString());
// Console.Read();
dev = bc.DiscoverDevices();
foreach (BluetoothDeviceInfo d in dev)
{
if (d.DeviceName == "ST21i")//my phone name
{
td=d;
break;
}
}
try
{
BluetoothAddress addr = td.DeviceAddress;
BluetoothListener bl = new BluetoothListener(addr, id);
bl.Start();
if (bl.AcceptSocket() != null)
Console.WriteLine("Success");
}
catch (Exception e)
{
Console.WriteLine("Exception : "+e.Message);
Console.Read();
}
}
}
}
and here is my android code :
public class MainActivity extends Activity {
BluetoothAdapter adapter;
BluetoothDevice bd;
BluetoothSocket sock;
OutputStream ostr;
int REQUEST_ENABLE_BT;
String str="5C:AC:4C:DD:CC:0D";
private static final UUID id=UUID.fromString("00112233-4455-6677-8899- aabbccddeeff");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter=BluetoothAdapter.getDefaultAdapter();
if (!adapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
Button b = (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "clicked button", Toast.LENGTH_LONG).show();
try
{
bd=adapter.getRemoteDevice(str); Toast.makeText(getApplicationContext(),"Server is running at "+bd.getName().toString()+"...", Toast.LENGTH_LONG).show();
sock=bd.createInsecureRfcommSocketToServiceRecord(id); sock.connect();
ostr=sock.getOutputStream();
ostr.write(0);
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(),e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
}
my problems are :
1) in pc i am getting an exception, the requested address is not valid in its context(so that server cant run )
2)in phone, the service discovery failed( because of unavailability of server)
how can i correct the server and run the program ?
i changed the bluetooth listener object's creation from
BluetoothListener bl = new BluetoothListener(addr, id); to
BluetoothListener bl = new BluetoothListener(id); and everything worked fine..

Tried to read incoming SMS content but getting Error in Blackberry

Hi friends i am trying to read incoming sms but getting warning like this . Invocation of questionable method: java.lang.String.(String) found in: mypackage.MyApp$ListeningThread.run()
Here is my code is
public class MyApp extends UiApplication {
//private ListeningThread listener;
public static void main(String[] args) {
MyApp theApp = new MyApp();
theApp.enterEventDispatcher();
}
public MyApp() {
invokeAndWait(new Runnable() {
public void run() {
ListeningThread listener = new ListeningThread();
listener.start();
}
});
pushScreen(new MyScreen());
}
private static class ListeningThread extends Thread {
private boolean _stop = false;
private DatagramConnection _dc;
public synchronized void stop() {
_stop = true;
try {
_dc.close(); // Close the connection so the thread returns.
} catch (IOException e) {
System.err.println(e.toString());
}
}
public void run() {
try {
_dc = (DatagramConnection) Connector.open("sms://");
for (;;) {
if (_stop) {
return;
}
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d);
String address = new String(d.getAddress());
String msg = new String(d.getData());
if(msg.startsWith("START")){
Dialog.alert("hello");
}
System.out.println("Message received: " + msg);
System.out.println("From: " + address);
System.exit(0);
}
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
}
Please correct me where i am wrong.Is possible give me some code to read incoming sms content in blackberry.
A few points about your code:
That invokeAndWait call to launch a thread makes no sense. It doesn't harm, but is kind of waste. Use that method only to perform UI related operations.
You should try using "sms://:0" as param for Connector.open. According to the docs, a parameter with the form {protocol}://[{host}]:[{port}] will open the connection in client mode (which makes sense, since you are on the receiving part), whereas not including the host part will open it in server mode.
Finally, if you can't get it working, you could use instead the third method specified in this tutorial, which you probably have already read.
The error you quoted is complaining about the use of the String constructor that takes a string argument. Since strings are immutable in Java-ME, this is just a waste. You can use the argument string directly:
Invocation of questionable method: java.lang.String.(String) found in: mypackage.MyApp$ListeningThread.run()
//String address = new String(d.getAddress());
String address = d.getAddress();
// getData() returns a byte[], so this is a different constructor
// However, this leaves the character encoding unspecified, so it
// will default to cp1252, which may not be what you want
String msg = new String(d.getData());

Resources