Spring Security groupRoleAttribute of LDAP - spring-security-ldap

my ldap tree structure as following. How can i configure groupRoleAttribute to get sibling records/tree (RolesList)?
ProfilesList
Profile1
UsersList
uniqueMember=User1
uniqueMember=User2
RolesList
uniqueMember=Role1
uniqueMember=Role2
Profile2
UsersList
uniqueMember=User3
uniqueMember=User4
RolesList
uniqueMember=Role3
uniqueMember=Role4
<beans:bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator" id="ldapAuthoritiesPopulator">
<beans:constructor-arg ref="contextSource" />
<beans:constructor-arg value="ou=ProfilesList"/>
<beans:property name="groupRoleAttribute" value="uniqueMember,cn=RolesList" />
<beans:property name="groupSearchFilter" value="(&(cn=UsersList)(uniqueMember={0}))"/>
</beans:bean>

I end up writing my own CustomLdapAuthoritiesPopulator program to support this requirement. Hope it will help someone in the future.
<beans:bean class="com.ldap.security.CustomLdapAuthoritiesPopulator" id="LdapAuthoritiesPopulator">
<beans:constructor-arg ref="contextSource" />
<beans:constructor-arg value="ou=Profile,ou=company"/>
</beans:bean>
The following are my CustomLdapAuthoritiesPopulator.
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.naming.Name;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextMapper;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
public class CustomLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
private static final Logger logger = LoggerFactory.getLogger(CustomLdapAuthoritiesPopulator.class);
private final LdapTemplate ldapTemplate;
private String groupSearchBase;
public CustomLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
this.ldapTemplate = new LdapTemplate(contextSource);
this.groupSearchBase = groupSearchBase;
}
#Override
public final Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations user, String username) {
if (groupSearchBase == null) {
return new HashSet<GrantedAuthority>();
}
logger.debug("Getting authorities for user " + user.getNameInNamespace());
LdapQuery query = query().base(groupSearchBase)
.where("cn").is("UsersList")
.and("objectclass").is("groupOfUniqueNames")
.and("uniqueMember").is(user.getNameInNamespace());
logger.debug("query: " + query.toString());
List<String> profileUsersList = ldapTemplate.search(query, new ProfileUsersListContextMapper());
List<String[]> rolesList = new LinkedList<String[]>();
for (String profile : profileUsersList) {
query = query().base("ou="+profile+","+groupSearchBase)
.where("cn").is("RolesList");
List<String[]> profileRolesList = ldapTemplate.search(query, new ProfileRolesListContextMapper());
rolesList.addAll(profileRolesList);
}
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for (String[] roles : rolesList) {
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
}
return authorities;
}
private static class ProfileUsersListContextMapper extends AbstractContextMapper<String> {
public String doMapFromContext(DirContextOperations context) {
String usersList = null;
Name dn = context.getDn();
if (!dn.isEmpty()) {
if (dn.size() > 3) {
usersList = (dn.get(2).split("="))[1];
}
}
return usersList;
}
}
private static class ProfileRolesListContextMapper extends AbstractContextMapper<String[]> {
public String[] doMapFromContext(DirContextOperations context) {
String[] roleNames = null;
String[] rolesList = context.getStringAttributes("uniqueMember");
if (rolesList != null) {
roleNames = new String[rolesList.length];
for (int i = 0; i < rolesList.length; i++) {
String[] attributes = roleNames[i].split(",");
for (int j = 0; j < attributes.length; j++) {
String[] keyValue = attributes[j].split("=");
if ("cn".equalsIgnoreCase(keyValue[0])) {
roleNames[i] = keyValue[1];
}
}
}
}
return roleNames;
}
}
}

Related

JUnit - Parameterized Test - Stopping compiling

Code that I writed below stopping compliling before contructor or #Before (depend of hiding). There is no errors and It can't run even one time.
I did it with tutorial:
https://www.tutorialspoint.com/junit/junit_parameterized_test.htm
Can somebody have idea what is wrong with this code?
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Scanner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
#RunWith(Parameterized.class)
public class ParametryzowaneTestyKarty {
private ArrayList<Karta> talia;
private String wynik;
private karty kartyy;
#Before
public void initialize() {
kartyy = new karty();
}
public ParametryzowaneTestyKarty(ArrayList<Karta> talia, String wynik) {
this.talia = talia;
this.wynik = wynik;
}
#Parameterized.Parameters
public static Collection wyniki() throws FileNotFoundException {
File plik22 = new File("...");
Scanner test = new Scanner(plik22);
while(test.hasNextLine()) {
ArrayList<Karta> talia = new ArrayList<>();
String wiersz = test.nextLine();
String[] parts = wiersz.split(",");
for(int i=0;i<10;i+=2) {
String part0 = parts[i];
String part1 = parts[i+1];
int kol=Integer.parseInt(part0);
int fig=Integer.parseInt(part1);
Kolor[] k = Kolor.values();
Kolor ko=k[kol];
Figura[] f = Figura.values();
Figura fi = f[fig];
talia.add(new Karta(ko, fi));
String w = parts[10];
Arrays.asList(new Object[][] {
{ talia, w },
});
}
}
return Arrays.asList();
}
#Test
public void TestParametryzowaneKarty() {
System.out.println("1");
System.out.println("Karty : " + talia);
assertEquals(wynik,
karty.check(talia));
}
}
It would help to know the exact error you are getting.
There are some issues with your code, as Arrays.asList() doesn't do what you are expecting, and as thus the method public static Collection wyniki() is returning a empty list.
The following code might fix the issue, but I doubt it as the talia list is reused for each row in the file that is being read.
#Parameterized.Parameters
public static Collection wyniki() throws FileNotFoundException {
File plik22 = new File("...");
Scanner test = new Scanner(plik22);
while(test.hasNextLine()) {
ArrayList<Karta> talia = new ArrayList<>();
ArrayList<Object[]> rows = new ArrayList<>();
String wiersz = test.nextLine();
String[] parts = wiersz.split(",");
for(int i=0;i<10;i+=2) {
String part0 = parts[i];
String part1 = parts[i+1];
int kol=Integer.parseInt(part0);
int fig=Integer.parseInt(part1);
Kolor[] k = Kolor.values();
Kolor ko=k[kol];
Figura[] f = Figura.values();
Figura fi = f[fig];
talia.add(new Karta(ko, fi));
String w = parts[10];
// new code
rows.add(new Object[]{talia, w} );
}
}
return rows;
}

can anyone explain how to initialize realm instance outside the Activity? for example while parsing json with volley and gson?

This is my Api class where json parsing is being done. But wherever I call Realm.getDefaultInstance(), at that line app stops running..Please help ...m stuck..Thanks in advance..
package com.portea.internal.api.realm_api;
import android.content.Context;
import android.util.Base64;
import android.util.Log;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.portea.internal.app.App;
import com.portea.internal.constants.Constants;
import com.portea.internal.constants.PrintLog;
import com.portea.internal.enums.Transaction;
import com.portea.internal.network.Network;
import com.portea.internal.realm_pojo_container.AppointmentPojos.Appointments;
import com.portea.internal.realm_pojo_container.AppointmentPojos.AppointmmentMainObject;
import com.portea.internal.utils.Utils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import io.realm.Realm;
import io.realm.RealmList;
/**
* Created by Dipti on 26/3/17.
*/
public class ApiAppointment extends Request<JSONObject> {
private Response.Listener<JSONObject> listener;
private boolean isSubordinateAppointment = false;
private boolean forceReload = false;
String Tag = "ApiAppointment";
// RealmList<AppointmmentMainObject> realmObjList =null;
RealmList<AppointmmentMainObject> inpList = null;
Collection<AppointmmentMainObject> realmApts;
private Context context;
private Realm my_realm;//where to initialize this realm instance and where to close it
AppointmmentMainObject obj1, obj2;
public ApiAppointment(Response.Listener<JSONObject> listener, Response.ErrorListener errorListener, String url) {
super(Request.Method.GET, getApiUrl(url), errorListener);
this.listener = listener;
setRetryPolicy(new DefaultRetryPolicy(60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
public ApiAppointment(Response.Listener<JSONObject> listener, Response.ErrorListener errorListener, String url, boolean forceReload) {
super(Request.Method.GET, getApiUrl(url), errorListener);
this.listener = listener;
this.forceReload = forceReload;
setRetryPolicy(new DefaultRetryPolicy(60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
Log.v(Tag + "dip", "" + getApiUrl(url));
}
public ApiAppointment(Response.Listener<JSONObject> listener, Response.ErrorListener errorListener, String url, String append) {
super(Request.Method.GET, getApiUrl(url) + append, errorListener);
this.listener = listener;
isSubordinateAppointment = true;
setRetryPolicy(new DefaultRetryPolicy(60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
Log.v(Tag, "" + getApiUrl(url) + append);
}
public ApiAppointment(Response.Listener<JSONObject> listener, Response.ErrorListener errorListener, String url, String append, boolean forceReload) {
super(Request.Method.GET, getApiUrl(url) + append, errorListener);
this.listener = listener;
this.forceReload = forceReload;
isSubordinateAppointment = true;
setRetryPolicy(new DefaultRetryPolicy(60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
Log.e(Tag, " " + getApiUrl(url) + append);
}
static String getApiUrl(String url_append) {
String lastSynced = App.getPref().get(Constants.STORAGE_KEY_LAST_APPOINTMENT_SYNCED);
if (lastSynced == null) {
lastSynced = "0";
}
if (url_append.length() > 0) {
lastSynced = "0";
}
return Network.getTxnPath(Transaction.APPOINTMENTS, "/get?user_id="
+ App.getUser().getUserId() + "&key=" + App.getUser().getKey()
+ "&version=" + App.version + "&last_synced=" + lastSynced + url_append);
}
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
Log.v(Tag, "========================================>>");
//Utils.sendLogoutBroadCast(App.getAppContext(), response.statusCode);
Gson gson = new GsonBuilder().create();
// Realm.init(App.getAppContext());
// my_realm = Realm.getDefaultInstance();
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
Log.i(Tag, jsonString);
JSONObject MainObject = new JSONObject(jsonString);
JSONArray dataObj = MainObject.getJSONArray("data");
for (int i = 0; i < dataObj.length(); i++) {
JSONObject singleUserObj = dataObj.getJSONObject(i);
JSONArray apointment = singleUserObj.getJSONArray("appointments");
for (int j = 0; j < apointment.length(); j++) {
JSONObject appointmentObj = apointment.getJSONObject(j);
// Appointments appointments=realm.createObjectFromJson(Appointments.class,appointmentObj);
// Log.v("Realmcheck",appointments.toString());
Type type = new TypeToken<RealmList<Appointments>>() {}.getType();
RealmList<Appointments> appointmentsObjList = gson.fromJson(apointment.toString(), type);
// List<Appointments> realm_copy_of_list=my_realm.copyToRealm(appointmentsObjList); Log.v("size", String.valueOf(appointmentsObjList.toString()));
RealmList<Appointments> apo = new RealmList<Appointments>();
Log.v("dipti", appointmentsObjList.get(j).toString());
// apo = (RealmList<Appointments>) my_realm.copyToRealm(appointmentsObjList);
}
}
// Log.v(Tag + "xx", AppointmmentMainObject.getClinicianName());
// Log.i("packageFee", String.valueOf(AppointmmentMainObject.getPackageFee()));
return Response.success(MainObject, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
PrintLog.d("e: " + e);
e.printStackTrace();
return Response.error(new ParseError(e));
} catch (JSONException je) {
PrintLog.d("je: " + je);
je.printStackTrace();
return Response.error(new ParseError(je));
} catch (NullPointerException ne) {
PrintLog.d("ne: " + ne);
return Response.error(new ParseError(ne));
}
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<>();
String creds = String.format("%s:%s", "stage", "d7kVzNZDqn");
String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.NO_WRAP);
params.put("Authorization", auth);
params.put("DEVICE_ID", App.deviceId);
return params;
}
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
try {
Utils.sendLogoutBroadCast(App.getAppContext(),
volleyError.networkResponse.statusCode);
Log.e("onErrorResponse", ""
+ volleyError.networkResponse.statusCode);
} catch (Exception e) {
e.printStackTrace();
}
return volleyError;
}
#Override
protected void deliverResponse(JSONObject response) {
}
}
I think your main problem is in the Realm initialization. You should have an application class and initialize your Realm in there. Like this:
public class BaseApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration config = new RealmConfiguration.Builder().build();
Realm.setDefaultConfiguration(config);
}
}
And then you can call my_realm = Realm.getDefaultInstance();
I hope this solves your problem.

Register Page Data store but no JSON encode received

I'm sure it's just a minor problem but I can't figure out what I need to change: I have a register page in my app. When the user inserts data, the data is passed to php script and stored in MYSQL database. The connection works as I get a new user in the db, but the json $success doesn't work - I want to make a Toast and change to a new activity if the user could register, but it does nothing when I click on the button.
Here is my Java Code:
package com.example.android.festivalapp;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class JetztRegistrieren extends Startseite {
Button bRegistrierungAbschliessen;
EditText etUserName, etUserMail, etUserPasswort, etUserPasswort2, etGeburtsdatum, etTelefonnummer;
RadioButton rbMaennlich, rbWeiblich;
RadioGroup rgGeschlecht;
protected String enteredname, enteredemail, enteredpassword, enteredpassword2, enteredGeb, enteredTelnr, userGender;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.jetzt_registrieren);
bRegistrierungAbschliessen = (Button) findViewById(R.id.bRegistrierungAbschliessen);
etUserName = (EditText) findViewById(R.id.etUserName);
etUserMail = (EditText) findViewById(R.id.etUserMail);
etUserPasswort = (EditText) findViewById(R.id.etUserPasswort);
etUserPasswort2 = (EditText) findViewById(R.id.etUserPasswort2);
etGeburtsdatum = (EditText) findViewById(R.id.etGeburtsdatum);
etTelefonnummer = (EditText) findViewById(R.id.etTelefonnummer);
rgGeschlecht = (RadioGroup) findViewById(R.id.rgGeschlecht);
rbMaennlich = (RadioButton) findViewById(R.id.rbMaennlich);
rbWeiblich = (RadioButton) findViewById(R.id.rbWeiblich);
bRegistrierungAbschliessen.setOnClickListener(new View.OnClickListener() {
//testen, ob Mail und Passwort ausgefüllt oder lang genug
#Override
public void onClick(View v) {
enteredname = etUserName.getText().toString();
enteredemail = etUserMail.getText().toString();
enteredpassword = etUserPasswort.getText().toString();
enteredpassword2 = etUserPasswort2.getText().toString();
enteredTelnr = etTelefonnummer.getText().toString();
enteredGeb = etGeburtsdatum.getText().toString();
userGender = ((RadioButton) findViewById(rgGeschlecht.getCheckedRadioButtonId())).getText().toString();
String serverUrl = "http://www.pou-pou.de/stagedriver/android/register.php";
AsyncDataClass asyncRequestObject = new AsyncDataClass();
asyncRequestObject.execute(serverUrl, enteredname, enteredemail, enteredpassword, enteredpassword2, enteredTelnr, enteredGeb, userGender);
if (enteredname.equals("") || enteredemail.equals("") || enteredpassword.equals("") || enteredpassword2.equals("") || enteredGeb.equals("")) {
Toast.makeText(JetztRegistrieren.this, "Bitte alle Felder ausfüllen", Toast.LENGTH_LONG).show();
return;
}
}
class AsyncDataClass extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 5000);
HttpConnectionParams.setSoTimeout(httpParameters, 5000);
HttpClient httpClient = new DefaultHttpClient(httpParameters);
HttpPost httpPost = new HttpPost("http://www.pou-pou.de/stagedriver/android/register.php");
String jsonResult = "";
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("enteredname", params[1]));
nameValuePairs.add(new BasicNameValuePair("enteredemail", params[2]));
nameValuePairs.add(new BasicNameValuePair("enteredpassword", params[3]));
nameValuePairs.add(new BasicNameValuePair("enteredpassword2", params[4]));
nameValuePairs.add(new BasicNameValuePair("enteredTelnr", params[5]));
nameValuePairs.add(new BasicNameValuePair("enteredGeb", params[6]));
nameValuePairs.add(new BasicNameValuePair("userGender", params[7]));
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpClient.execute(httpPost);
jsonResult = inputStreamToString(response.getEntity().getContent()).toString();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return jsonResult;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
int jsonResult = returnParsedJsonObject(result);
if (jsonResult == 0) {
Toast.makeText(JetztRegistrieren.this, "Passwörter stimmen nicht überein", Toast.LENGTH_LONG).show();
return;
}
if (jsonResult == 1) {
Intent intent = new Intent(JetztRegistrieren.this, Startseite.class);
Toast.makeText(JetztRegistrieren.this, "Willkommen bei Stage Driver! Jetzt loslegen.", Toast.LENGTH_LONG).show();
startActivity(intent);
return;
}
System.out.println("Resulted Value: " + result);
}
private StringBuilder inputStreamToString(InputStream is) {
String rLine = "";
StringBuilder answer = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
try {
while ((rLine = br.readLine()) != null) {
answer.append(rLine);
}
} catch (IOException e) {
e.printStackTrace();
}
return answer;
}
}
private int returnParsedJsonObject(String result) {
JSONObject resultObject = null;
int returnedResult = 2;
try {
resultObject = new JSONObject(result);
returnedResult = resultObject.getInt("success");
} catch (JSONException e) {
e.printStackTrace();
}
return returnedResult;
}
});
}
}
And this is the php file:
<?php
include('config.php');
session_start();
$name = $_POST["enteredname"];
$pass = $_POST["enteredpassword"];
$pass2 = $_POST["enteredpassword2"];
$email = $_POST["enteredemail"];
$tel = $_POST["enteredTelnr"];
$geb = $_POST["enteredGeb"];
$gender = $_POST["userGender"];
if($pass != $pass2) {
$success = 0;
}
else {
$hash = md5($pass);
$speichern = "INSERT INTO user (user_name, user_pw, user_mail, user_tel, user_geb, user_geschl)
VALUES('$name', '$hash', '$email', '$tel', '$geb', '$gender');";
mysql_query($speichern) or die(mysql_error());
if($speichern) {
$success = 1;
}
}
echo json_encode($success);
?>
Thanks in advance!
The problem is that you are not returning an HTTP header for the content type you are returning.
Add this to the top of your php script:
header("Content-Type: application/json;charset=utf-8");
So I think I found the error, in the log it says the jsonresult cannot be converted, it's because in the php script I wrote $success = 0; but instead I think it has to be something like $response["success"] = 0;

Upload pdf in HTML and Deserialize json file

I'm trying to upload a file in html and then send it to my database via restangular.
My frontend is a combination of angular with typescript but the upload is a form.
<form enctype="multipart/form-data">
<fieldset class="form-group" ng-repeat="field in $ctrl.metadata.fields">
<label ng-if="field.inputType !== 'hidden'" for="{{field.propertyKey}}"><strong>{{field.name}}</strong></label>
<input ng-if="field.inputType !== 'select' && field.inputType !== 'file'" class="form-control" type="{{field.inputType}}" name="{{field.propertyKey}}" id="{{field.propertyKey}}" ng-model="$ctrl.data[field.propertyKey]"/>
<input ng-if="field.inputType === 'file'" class="form-control" ngf-select type="{{field.inputType}}" name="{{field.propertyKey}}" id="{{field.propertyKey}}" ng-model="$ctrl.data[field.propertyKey]"/>
<sp-dropdown ng-if="field.inputType === 'select'" value="$ctrl.data[field.propertyKey]" api-domain="field.linkedObjectApiDomain" linked-object-name="field.linkedObjectName"></sp-dropdown>
</fieldset>
<button class="btn btn-primary" ng-click="$ctrl.save({item: $ctrl.data})">Save</button>
<button ng-if="$ctrl.metadata.buttons.hasOpen" class="btn btn-primary" ng-click="$ctrl.open()">Open</button>
</form>
I did the databinding of the file with ng-file-upload.
Upon saving we enter this typescript save method.
public save(item: any): any {
console.log("item to save is ", item);
console.log("rapport is ", item["rapport"]);
if (item.id === undefined) {
this.restService.save(this.metadata.apiDomain, item).then((addedItem: any) => {
toastr.success(`${addedItem.naam} successfully created.`, `Overzicht Dossiers Created`);
});
} else {
this.restService.update(this.metadata.apiDomain, item).then((updatedItem: any) => {
toastr.success(`${updatedItem.naam} successfully updated.`, `Overzicht Dossiers Updated`);
});
}
}
The second log with the file gives the json:
lastModified:1463402787393
lastModifiedDate:Mon May 16 2016 14:46:27 GMT+0200 (Romance (zomertijd))
name:"Rapport.pdf"
size:83605
type:"application/pdf"
upload:Promise
webkitRelativePath:""
__proto__:File
On server side I'm using a spring project which I didn't set up myself but the important files are my class which should store this data
Dossier
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package be.ugent.lca.data.entities;
import be.ugent.sherpa.entity.BaseEntity;
import java.sql.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
/**
*
* #author Sam
*/
#Entity
//#JsonDeserialize(using = DossierDeserializer.class)
//#JsonSerialize(using = DossierSerializer.class)
public class Dossier extends BaseEntity{
private String externDossierNr;
private String internDossierNr;
private Date datum;
private Boolean doc;
private Date refKlantDatum;
private String refKlantVerwijzing;
private String verantw;
#OneToOne(fetch=FetchType.LAZY, mappedBy="dossier")
private Offerte offerte;
private String status;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "persoon")
private Persoon persoon;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "OrganisatieFirma")
private OrganisatieFirma organisatieFirma;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "OrganisatieIntern")
private OrganisatieIntern organisatieIntern;
#Lob
#Column(length=100000)
private byte[] rapport;
public Offerte getOfferte() {
return offerte;
}
public void setOfferte(Offerte offerte) {
this.offerte = offerte;
}
public byte[] getRapport() {
return rapport;
}
public void setRapport(byte[] rapport) {
this.rapport = rapport;
}
public OrganisatieFirma getOrganisatieFirma() {
return organisatieFirma;
}
public String getExternDossierNr() {
return externDossierNr;
}
public void setExternDossierNr(String externDossierNr) {
this.externDossierNr = externDossierNr;
}
public String getInternDossierNr() {
return internDossierNr;
}
public void setInternDossierNr(String internDossierNr) {
this.internDossierNr = internDossierNr;
}
public void setOrganisatieFirma(OrganisatieFirma organisatieFirma) {
this.organisatieFirma = organisatieFirma;
}
public OrganisatieIntern getOrganisatieIntern() {
return organisatieIntern;
}
public void setOrganisatieIntern(OrganisatieIntern organisatieIntern) {
this.organisatieIntern = organisatieIntern;
}
public Persoon getPersoon() {
return persoon;
}
public void setPersoon(Persoon persoon) {
this.persoon = persoon;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getDatum() {
return datum;
}
public void setDatum(Date datum) {
this.datum = datum;
}
public Date getRefKlantDatum() {
return refKlantDatum;
}
public void setRefKlantDatum(Date refKlantDatum) {
this.refKlantDatum = refKlantDatum;
}
public String getRefKlantVerwijzing() {
return refKlantVerwijzing;
}
public void setRefKlantVerwijzing(String refKlantVerwijzing) {
this.refKlantVerwijzing = refKlantVerwijzing;
}
public String getVerantw() {
return verantw;
}
public void setVerantw(String verantw) {
this.verantw = verantw;
}
public Boolean getDoc() {
return doc;
}
public void setDoc(Boolean doc) {
this.doc = doc;
}
}
and my repository for this class
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package be.ugent.lca.data.repository;
import be.ugent.lca.data.entities.Dossier;
import be.ugent.lca.data.query.DossierQuery;
import be.ugent.sherpa.repository.RestRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
/**
*
* #author Sam
*/
#RepositoryRestResource(collectionResourceRel = "dossiers", path = "dossiers")
public interface DossierRepository extends RestRepository<Dossier, DossierQuery<?>>{
}
When trying to save a file to my database the server gives this exception
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of byte[] out of START_OBJECT token
This led me to believe that I have to write my own deserializer for Dossier
Thus:
package be.ugent.lca.data.entities.deserializers;
import be.ugent.lca.data.entities.Dossier;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
public class DossierDeserializer extends JsonDeserializer {
#Override
public Dossier deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode root = oc.readTree(jsonParser);
Dossier dossier = new Dossier();
dossier.setExternDossierNr(root.get("externDossierNr").asText());
dossier.setInternDossierNr(root.get("internDossierNr").asText());
return dossier;
}
}
But my problem is that I don't know how exactly to deserialize the file json, since writing out root.get("rapport") gives back an empty string.
Any help would be much appreciated.
I've worked out the file upload.
First of all I split the file upload from the rest of my data so I won't have to rewrite the automatic deserialization for everything that does work.
this.restService.save(this.metadata.apiDomain, item).then((addedItem: any) => {
toastr.success(`${addedItem.naam} successfully created.`, `Overzicht Dossiers Created`);
console.log("created item ", addedItem);
var fd = new FormData();
fd.append("rapport", item["rapport"]);
this.restService.one('dossiers/' + addedItem.id + '/rapport').withHttpConfig({transformRequest: angular.identity}).customPOST(fd, '', undefined, {'Content-Type': undefined}).then(
(addedDossier: any) => {
console.log("posted dossier ", addedDossier);
}
);
});
In the callback of my normal save I do the custom post to dossiers/{id}/rapport for this I need a custom controller.
#BasePathAwareController
#RequestMapping("/dossiers/{id}")
#ExposesResourceFor(Dossier.class)
public class DossierController {
The BasePathAwawareController makes sure that all automatically generated paths that you don't override keep existing.
#Autowired
private DossierRepository dossierRepository;
With this I inject my repository to connect to my database.
#RequestMapping(path = "/rapport", method = RequestMethod.POST)//,headers = "content-type=multipart/form-data")
public #ResponseBody String postRapport(#PathVariable("id") Long id,#RequestParam("rapport") MultipartFile file) {
String name = "rapport";
System.out.println("Entered custom file upload with id " + id);
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
Dossier dossier = dossierRepository.findOne(id);
dossier.setRapport(bytes);
dossierRepository.save(dossier);
return "You successfully uploaded " + name + " into " + name + "-uploaded !";
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
} else {
return "You failed to upload " + name + " because the file was empty.";
}
}
Like this I'm able to successfully upload my file.

Keep tags order using SnakeYAML

I'm trying to translate yaml files to json, but the translation re-orders the tags...
Ex, YAML source:
zzzz:
b: 456
a: dfff
aa:
s10: "dddz"
s3: eeee
bbb:
- b1
- a2
snakeYAML produces:
{
"aa": {
"s3": "eeee",
"s10":"dddz"
},
"bbb":[
"b1",
"a2"
],
"zzzz": {
"a": "dfff",
"b":456
}
}
Create following class in your code, this is a tweaked version from the SnakeYAML source that uses LinkedHashMap and LinkedHashSet that keep the insertion order instead of TreeMap and TreeSet which auto-sort them.
import java.beans.FeatureDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.introspector.*;
import org.yaml.snakeyaml.util.PlatformFeatureDetector;
public class CustomPropertyUtils extends PropertyUtils {
private final Map<Class<?>, Map<String, Property>> propertiesCache = new HashMap<Class<?>, Map<String, Property>>();
private final Map<Class<?>, Set<Property>> readableProperties = new HashMap<Class<?>, Set<Property>>();
private BeanAccess beanAccess = BeanAccess.DEFAULT;
private boolean allowReadOnlyProperties = false;
private boolean skipMissingProperties = false;
private PlatformFeatureDetector platformFeatureDetector;
public CustomPropertyUtils() {
this(new PlatformFeatureDetector());
}
CustomPropertyUtils(PlatformFeatureDetector platformFeatureDetector) {
this.platformFeatureDetector = platformFeatureDetector;
/*
* Android lacks much of java.beans (including the Introspector class, used here), because java.beans classes tend to rely on java.awt, which isn't
* supported in the Android SDK. That means we have to fall back on FIELD access only when SnakeYAML is running on the Android Runtime.
*/
if (platformFeatureDetector.isRunningOnAndroid()) {
beanAccess = BeanAccess.FIELD;
}
}
protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
if (propertiesCache.containsKey(type)) {
return propertiesCache.get(type);
}
Map<String, Property> properties = new LinkedHashMap<String, Property>();
boolean inaccessableFieldsExist = false;
switch (bAccess) {
case FIELD:
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
for (Field field : c.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
&& !properties.containsKey(field.getName())) {
properties.put(field.getName(), new FieldProperty(field));
}
}
}
break;
default:
// add JavaBean properties
try {
for (PropertyDescriptor property : Introspector.getBeanInfo(type)
.getPropertyDescriptors()) {
Method readMethod = property.getReadMethod();
if ((readMethod == null || !readMethod.getName().equals("getClass"))
&& !isTransient(property)) {
properties.put(property.getName(), new MethodProperty(property));
}
}
} catch (IntrospectionException e) {
throw new YAMLException(e);
}
// add public fields
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
for (Field field : c.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
if (Modifier.isPublic(modifiers)) {
properties.put(field.getName(), new FieldProperty(field));
} else {
inaccessableFieldsExist = true;
}
}
}
}
break;
}
if (properties.isEmpty() && inaccessableFieldsExist) {
throw new YAMLException("No JavaBean properties found in " + type.getName());
}
System.out.println(properties);
propertiesCache.put(type, properties);
return properties;
}
private static final String TRANSIENT = "transient";
private boolean isTransient(FeatureDescriptor fd) {
return Boolean.TRUE.equals(fd.getValue(TRANSIENT));
}
public Set<Property> getProperties(Class<? extends Object> type) {
return getProperties(type, beanAccess);
}
public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess) {
if (readableProperties.containsKey(type)) {
return readableProperties.get(type);
}
Set<Property> properties = createPropertySet(type, bAccess);
readableProperties.put(type, properties);
return properties;
}
protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess) {
Set<Property> properties = new LinkedHashSet<>();
Collection<Property> props = getPropertiesMap(type, bAccess).values();
for (Property property : props) {
if (property.isReadable() && (allowReadOnlyProperties || property.isWritable())) {
properties.add(property);
}
}
return properties;
}
public Property getProperty(Class<? extends Object> type, String name) {
return getProperty(type, name, beanAccess);
}
public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess) {
Map<String, Property> properties = getPropertiesMap(type, bAccess);
Property property = properties.get(name);
if (property == null && skipMissingProperties) {
property = new MissingProperty(name);
}
if (property == null) {
throw new YAMLException(
"Unable to find property '" + name + "' on class: " + type.getName());
}
return property;
}
public void setBeanAccess(BeanAccess beanAccess) {
if (platformFeatureDetector.isRunningOnAndroid() && beanAccess != BeanAccess.FIELD) {
throw new IllegalArgumentException(
"JVM is Android - only BeanAccess.FIELD is available");
}
if (this.beanAccess != beanAccess) {
this.beanAccess = beanAccess;
propertiesCache.clear();
readableProperties.clear();
}
}
public void setAllowReadOnlyProperties(boolean allowReadOnlyProperties) {
if (this.allowReadOnlyProperties != allowReadOnlyProperties) {
this.allowReadOnlyProperties = allowReadOnlyProperties;
readableProperties.clear();
}
}
public boolean isAllowReadOnlyProperties() {
return allowReadOnlyProperties;
}
/**
* Skip properties that are missing during deserialization of YAML to a Java
* object. The default is false.
*
* #param skipMissingProperties
* true if missing properties should be skipped, false otherwise.
*/
public void setSkipMissingProperties(boolean skipMissingProperties) {
if (this.skipMissingProperties != skipMissingProperties) {
this.skipMissingProperties = skipMissingProperties;
readableProperties.clear();
}
}
public boolean isSkipMissingProperties() {
return skipMissingProperties;
}
}
Then, create your Yaml instance like this:
DumperOptions options = new DumperOptions();
CustomPropertyUtils customPropertyUtils = new CustomPropertyUtils();
Representer customRepresenter = new Representer();
customRepresenter.setPropertyUtils(customPropertyUtils);
Yaml yaml = new Yaml(customRepresenter, options);
Profit!
The keeping of property order depends on java implementation and is not guaranteed.
In order to control the yaml generation you will need to implement your CustomRepresenter overriding the getProperties , see the example below:
package io.github.rockitconsulting.test.rockitizer.configuration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.representer.Representer;
/**
* Custom implementation of {#link Representer} and {#link Comparator}
* to keep the needed order of javabean properties of model classes,
* thus generating the understandable yaml
*
*/
public class ConfigurationModelRepresenter extends Representer {
public ConfigurationModelRepresenter() {
super();
}
public ConfigurationModelRepresenter(DumperOptions options) {
super(options);
}
protected Set<Property> getProperties(Class<? extends Object> type) {
Set<Property> propertySet;
if (typeDefinitions.containsKey(type)) {
propertySet = typeDefinitions.get(type).getProperties();
}
propertySet = getPropertyUtils().getProperties(type);
List<Property> propsList = new ArrayList<>(propertySet);
Collections.sort(propsList, new BeanPropertyComparator());
return new LinkedHashSet<>(propsList);
}
class BeanPropertyComparator implements Comparator<Property> {
public int compare(Property p1, Property p2) {
if (p1.getType().getCanonicalName().contains("util") && !p2.getType().getCanonicalName().contains("util")) {
return 1;
} else if(p2.getName().endsWith("Name")|| p2.getName().equalsIgnoreCase("name")) {
return 1;
} else {
return -1;
} // returning 0 would merge keys
}
}
}
The below snippet shows the usage of the newly created class to generate the yaml structure:
DumperOptions options = new DumperOptions();
ConfigurationModelRepresenter customRepresenter = new ConfigurationModelRepresenter();
Yaml yaml = new Yaml(customRepresenter, options);
StringWriter writer = new StringWriter();
yaml.dump(suite, writer);
FileWriter fw = new FileWriter(rootPathTestSrc + "config_gen.yml", false);
fw.write(writer.toString());
fw.close();
This approach is much cleaner, comparing to the one suggested above.