Java WS file upload service - soap

I am currently working on a Java Web Service project. It is deployed on an Apache Tomcat 7 server and I need to provide a service for uploading files to the server. Also, the web service should be available to systems that may not use java. Thus, I need to make my web service universally available for all clients (i.e. C#, php etc.).
After browsing the web, I have found many solutions, but none of them does not explain how can I fulfill the aforementioned criteria. To be more specific, I have come across MTOM and Java WS Annotations that are referenced to be essential in order to specify universally accepted data stuctures, such as Java's DataHandler.
Let me post a sample code of my web service:
public class DataFileServer extends AbstractFileServer {
private int _buffer_size;
public DataFileServer() {
_buffer_size = 100000;
}
public DataFileServer(int bufferSize) {
_buffer_size = bufferSize;
}
#Override
public void uploadFile(String AbsoluteFilePath, FileObject FileInfo) {
DataHandler _handler = FileInfo.getHandler();
try {
InputStream is = _handler.getInputStream();
OutputStream os = new FileOutputStream(new File(AbsoluteFilePath +
"/" + FileInfo.getName() + "." +
FileInfo.getType()));
byte[] b = new byte[_buffer_size];
int bytesRead = 0;
while((bytesRead = is.read()) != -1) {
os.write(b, 0, bytesRead);
}
}catch(IOException e) {
e.printStackTrace();
}
}
}
The DataFileServer is the class that will to the upload operation, and the Web Service interface will be as follows:
#WebService()
public class ETL_WS {
private DataHandler _handler;
private String server_dir = "/home/user/Desktop/FileServer";
public int fileUpload(String FileName, DataHandler CsvFile) {
FileObject _file = new FileObject(FileName, CsvFile);
_handler.uploadFile(this.server_dir, _file);
return 0;
}
}
My question is, how am I going to ensure that the DataHandler object provided to my web service is going to be of the right type. Also, can I improve the security and the performance of the file upload operation with any way?
Thank you

Related

Spring boot rest service to download a zip file which contains multiple file

I am able to download a single file but how I can download a zip file which contain multiple files.
Below is the code to download a single file but I have multiples files to download. Any help would greatly appreciated as I am stuck on this for last 2 days.
#GET
#Path("/download/{fname}/{ext}")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(#PathParam("fname") String fileName,#PathParam("ext") String fileExt){
File file = new File("C:/temp/"+fileName+"."+fileExt);
ResponseBuilder rb = Response.ok(file);
rb.header("Content-Disposition", "attachment; filename=" + file.getName());
Response response = rb.build();
return response;
}
Here is my working code I have used response.getOuptStream()
#RestController
public class DownloadFileController {
#Autowired
DownloadService service;
#GetMapping("/downloadZip")
public void downloadFile(HttpServletResponse response) {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=download.zip");
response.setStatus(HttpServletResponse.SC_OK);
List<String> fileNames = service.getFileName();
System.out.println("############# file size ###########" + fileNames.size());
try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
for (String file : fileNames) {
FileSystemResource resource = new FileSystemResource(file);
ZipEntry e = new ZipEntry(resource.getFilename());
// Configure the zip entry, the properties of the file
e.setSize(resource.contentLength());
e.setTime(System.currentTimeMillis());
// etc.
zippedOut.putNextEntry(e);
// And the content of the resource:
StreamUtils.copy(resource.getInputStream(), zippedOut);
zippedOut.closeEntry();
}
zippedOut.finish();
} catch (Exception e) {
// Exception handling goes here
}
}
}
Service Class:-
public class DownloadServiceImpl implements DownloadService {
#Autowired
DownloadServiceDao repo;
#Override
public List<String> getFileName() {
String[] fileName = { "C:\\neon\\FileTest\\File1.xlsx", "C:\\neon\\FileTest\\File2.xlsx", "C:\\neon\\FileTest\\File3.xlsx" };
List<String> fileList = new ArrayList<>(Arrays.asList(fileName));
return fileList;
}
}
Use these Spring MVC provided abstractions to avoid loading of whole file in memory.
org.springframework.core.io.Resource & org.springframework.core.io.InputStreamSource
This way, your underlying implementation can change without changing controller interface & also your downloads would be streamed byte by byte.
See accepted answer here which is basically using org.springframework.core.io.FileSystemResource to create a Resource and there is a logic to create zip file on the fly too.
That above answer has return type as void, while you should directly return a Resource or ResponseEntity<Resource> .
As demonstrated in this answer, loop around your actual files and put in zip stream. Have a look at produces and content-type headers.
Combine these two answers to get what you are trying to achieve.
public void downloadSupportBundle(HttpServletResponse response){
File file = new File("supportbundle.tar.gz");
Path path = Paths.get(file.getAbsolutePath());
logger.debug("__path {} - absolute Path{}", path.getFileName(),
path.getRoot().toAbsolutePath());
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=supportbundle.tar.gz");
response.setStatus(HttpServletResponse.SC_OK);
System.out.println("############# file name ###########" + file.getName());
try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
FileSystemResource resource = new FileSystemResource(file);
ZipEntry e = new ZipEntry(resource.getFilename());
e.setSize(resource.contentLength());
e.setTime(System.currentTimeMillis());
zippedOut.putNextEntry(e);
StreamUtils.copy(resource.getInputStream(), zippedOut);
zippedOut.closeEntry();
zippedOut.finish();
} catch (Exception e) {
}
}

Using shared UDP Socket in Jersey REST Api

I am developing a simple rest service with jersey and jetty, which gets some requests from the clients and sends the prepared request further to another software (which is not made by me). The software sends some messages back that must be analyzed and sends back to the client. Therefore I've developed a new thread called SocketReceiver which starts after the request ist send to the software. This class uses a helper class which stores the connection to the udp socket (implemented as singleton). The solution works perfect as long as I have only one request per time. If two requests (from two separate clients) are made, there is a concurrency issue and no response is written back to the client by jersey.
Here's the run method of the socket receiver:
#Override
public void run() {
byte[] message = new byte[9999];
DatagramPacket p = new DatagramPacket(message, message.length);
while (returnedObject == null) {
logger.debug("Waiting for incomming message ...");
try {
onDemandInfoServer.getDataRadioSocket().receive(p);
MessageFrame messageFrame = new MessageFrame(message);
// CHECK Length (2. and 3. byte) and CODE (first byte)
if ((message[1] == 0 && message[2] == 0) || !(message[0] == DC_Constants.D_Code
|| message[0] == DC_Constants.G_Code || message[0] == DC_Constants.P_Code)) {
logger.warn("wrong message!");
continue;
}
int length = messageFrame.getLength();
if (length > message.length) {
logger.warn("wrong message!");
continue;
}
byte[] msg = new byte[length];
System.arraycopy(message, 0, msg, 0, length);
logger.debug(LogHelper.getDataAsString(msg));
if (message[0] == DC_Constants.P_Code) {
msg = messageIsParanet(messageFrame);
}
MessageFrame msgFrame = new MessageFrame(msg);
if (!msgFrame.isAcknowledge()) {
newMessageFromDataradio(msgFrame);
}
} catch (IOException e) {
// error handling
}
}
asyncResponse.resume(returnedObject); // returned objects sets when messages being analyszed
}
And here's a example jersey rest service:
#Path("/trips")
public class RequestTripListService extends BaseRestService {
private static final Logger logger = Logger.getLogger(RequestTripListService.class);
#POST
#Compress
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void logonToBlock(TripListRequest tripListRequest, #Suspended final AsyncResponse asyncResponse,
#Context HttpServletRequest request) throws IOException {
// Send message via udp
byte[] logonTelegramm = createLogonTelegramm(sequenceNumber);
getOnDemandInfoServer().sendToDataRadio(logonTelegramm);
// Build a new socket receiver and execute it
JourneyOnDemandInfo info = new JourneyOnDemandInfo(terminal, tripListRequest.getBlockNo(), 0, 0, sequenceNumber);
SocketReceiver<TripListUpdate> socketReceiver = new SocketReceiver<>(asyncResponse,
getOnDemandInfoServer(), getXmlCodec(), info, TripListUpdate.class);
socketReceiver.start();
}
}
What is the correct and thread safe way to use a shared udp socket?
How can I solve my problem described above?
Thanks for your help!

Display available REST resources in development stage

I was wondering wheather it's possible to output the available REST paths of a Java EE web app (war deplopyment) as a summary on a page. Of course, for security reasons only in development mode. Is there something available for this?
Thanks
Here is a quick + dirty example which will return all paths for the scanned ResourceClasses:
Path("/paths")
public class PathResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response paths(#Context HttpServletRequest request) {
StringBuilder out = new StringBuilder();
String applicationPath = "/"; // the path your Application is mapped to
#SuppressWarnings("unchecked")
Map<String, ResteasyDeployment> deployments = (Map<String, ResteasyDeployment>) request.getServletContext().getAttribute("resteasy.deployments");
ResteasyDeployment deployment = deployments.get(applicationPath);
List<String> scannedResourceClasses = deployment.getScannedResourceClasses();
try {
for (String className : scannedResourceClasses) {
Class<?> clazz = Class.forName(className);
String basePath = "";
if (clazz.isAnnotationPresent(Path.class)) {
basePath = clazz.getAnnotation(Path.class).value();
}
out.append(String.format("BasePath for Resource '%s': '%s'", className, basePath)).append('\n');
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Path.class)) {
String path = method.getAnnotation(Path.class).value();
out.append(String.format("Path for Method '%s': '%s'", method.getName(), basePath + path)).append('\n');
}
}
}
} catch(ClassNotFoundException ex) {
throw new IllegalArgumentException(ex);
}
return Response.ok(out).build();
}
}
For developers who are working with Eclipse. Simply use open the Project Exlorer view and see the list of available resources under JAX-RS Web Services. I'm positive there is something similar for other IDEs.

Value lost when transferring from server to client in silverlight

I am making a Silverlight app using WCF. I want to get the status of the hard-disks from remote servers and I am able to do that on the server side using a Management object. I have defined a wrapper class to hold the data of the hard-disks and store the objects in a list which I return.
Earlier, when the wrapper class was in the server project, it worked fine. However, when I transferred the class to a class library project in the same solution, the asynchronous call-completed event handler on the client side now gives me an event argument that is empty, i.e. an empty list
I tried debugging both the server and client code, and I see that the server creates the list properly and accesses the disk objects nicely. But the client code simply shows the list to be of size 0.
My client code is:
private void getDiskStatus()
{
diskSpaceStatus.Text = "Running...";
if (server == string.Empty)
{
server = "localhost";
}
diskServer.Text = server;
LogReaderClient proxy = new LogReaderClient();
proxy.getDiskSpaceCompleted += new EventHandler<getDiskSpaceCompletedEventArgs>(proxy_getDiskSpaceCompleted);
proxy.getDiskSpaceAsync(server);
}
void proxy_getDiskSpaceCompleted(object sender, getDiskSpaceCompletedEventArgs e)
{
diskSpaceStatus.Text = "Completed";
try
{
List<uDisk> udisks = new List<uDisk>();
foreach (Disk d in e.Result)
{
uDisk ud = new uDisk(d);
udisks.Add(ud);
}
diskTable.ItemsSource = udisks;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Where uDisk is another wrapper class for the client side.
My server code is:
[OperationContract]
public List<Disk> getDiskSpace(string server)
{
ConnectionOptions conn = new ConnectionOptions();
ManagementScope scope = new ManagementScope("\\\\" + server + "\\root\\cimv2", conn);
try
{
scope.Connect();
}
catch (Exception ex)
{
error = ex.Message;
}
ObjectQuery oq = new ObjectQuery("select FreeSpace, Size, Name from Win32_LogicalDisk where DriveType=3");
ManagementObjectSearcher search = new ManagementObjectSearcher(scope, oq);
ManagementObjectCollection moc = search.Get();
List<Disk> disks = new List<Disk>();
Disk d;
foreach (ManagementObject mo in moc)
{
d = new Disk(mo);
disks.Add(d);
}
return disks;
}
And the server wrapper class is:
namespace LogFilter.DataObjects
{
[DataContract]
public class Disk
{
[DataMember]
public string name;
[DataMember]
public double freeSpace;
[DataMember]
public double size;
[DataMember]
public double percentFree;
public Disk()
{}
public Disk(ManagementObject mo)
{
this.name = Convert.ToString(mo["Name"]);
this.freeSpace = Convert.ToDouble(mo["FreeSpace"]);
this.size = Convert.ToDouble(mo["Size"]);
this.percentFree = freeSpace * 100 / size;
}
}
}
The wrapper class is in the namespace LogFilter.DataObjects and the Server code is in the namespace LogFilter.Web.
Can anyone provide a solution to this?
Also can someone please give me a resource as to how to set the transfermode in a Silverlight application to Buffered?

Generating random session id whenever user uses login() in web services

Am new to web services. Am trying to generate unique session id for every login that a user does, in web services.
What I thought of doing is,
Write a java file which has the login and logout method.
Generate WSDL file for it.
Then generate web service client(using Eclipse IDE), with the WSDl file which I generate.
Use the generated package(client stub) and call the methods.
Please let me know if there are any flaws in my way of implementation.
1. Java file with the needed methods
public String login(String userID, String password) {
if (userID.equalsIgnoreCase("sadmin")
&& password.equalsIgnoreCase("sadmin")) {
System.out.println("Valid user");
sid = generateUUID(userID);
} else {
System.out.println("Auth failed");
}
return sid;
}
private String generateUUID(String userID) {
UUID uuID = UUID.randomUUID();
sid = uuID.toString();
userSessionHashMap = new HashMap<String, String>();
userSessionHashMap.put(userID, sid);
return sid;
}
public void logout(String userID) {
Set<String> userIDSet = userSessionHashMap.keySet();
Iterator<String> iterator = userIDSet.iterator();
if (iterator.equals(userID)) {
userSessionHashMap.remove(userID);
}
}
2. Generated WSDL file
Developed the web service client from the wsdl.
4. Using the developed client stub.
public static void main(String[] args) throws Exception {
ClientWebServiceLogin objClientWebServiceLogin = new ClientWebServiceLogin();
objClientWebServiceLogin.invokeLogin();
}
public void invokeLogin() throws Exception {
String endpoint = "http://schemas.xmlsoap.org/wsdl/";
String username = "sadmin";
String password = "sadmin";
String targetNamespace = "http://WebServiceLogin";
try {
WebServiceLoginLocator objWebServiceLoginLocator = new WebServiceLoginLocator();
java.net.URL url = new java.net.URL(endpoint);
Iterator ports = objWebServiceLoginLocator.getPorts();
while (ports.hasNext())
System.out.println("ports Iterator size-->" + ports.next());
WebServiceLoginPortType objWebServiceLoginPortType = objWebServiceLoginLocator
.getWebServiceLoginHttpSoap11Endpoint();
String sid = objWebServiceLoginPortType.login(username, password);
System.out.println("sid--->" + sid);
} catch (Exception exception) {
System.out.println("AxisFault at creating objWebServiceLoginStub"
+ exception);
exception.printStackTrace();
}
}
On running the this file, I get the following error.
AxisFault
faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException
faultSubcode:
faultString: java.net.ConnectException: Connection refused: connect
faultActor:
faultNode:
faultDetail:
{http://xml.apache.org/axis/}stackTrace:java.net.ConnectException: Connection refused: connect
Can anyone suggest an alternate way of handling this task ? And what could probably be the reason for this error.
Web services are supposed to be stateless, so having "login" and "logout" web service methods doesn't make much sense.
If you want to secure web services calls unfortunately you have to code security into every call. In your case, this means passing the userId and password to every method.
Or consider adding a custom handler for security. Read more about handlers here.